gt:         int length = shortHexString.length();
context: /*
   * Copyright 2011-2021 Fraunhofer ISE
   *
   * This file is part of OpenMUC.
   * For more information visit http://www.openmuc.org
   *
   * OpenMUC is free software: you can redistribute it and/or modify
   * it under the terms of the GNU General Public License as published by
   * the Free Software Foundation, either version 3 of the License, or
   * (at your option) any later version.
   *
   * OpenMUC is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   * GNU General Public License for more details.
   *
   * You should have received a copy of the GNU General Public License
   * along with OpenMUC.  If not, see <http://www.gnu.org/licenses/>.
   *
   */
  package org.openmuc.framework.driver.iec60870.settings;
  
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Method;
  import java.net.InetAddress;
  import java.net.UnknownHostException;
  import java.nio.charset.StandardCharsets;
  import java.text.MessageFormat;
  import java.util.Locale;
  
  import org.openmuc.framework.config.ArgumentSyntaxException;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public abstract class GenericSetting {
  
      private static final String SEPARATOR = ";";
      private static final String PAIR_SEP = "=";
      private static final String PREFIX = "prefix";
      private static final String TYPE = "type";
      private static final String MANDATORY = "mandatory";
      private static final Locale LOCALE = Locale.ENGLISH;
  
      private static final Logger logger = LoggerFactory.getLogger(GenericSetting.class);
  
      @SuppressWarnings("unchecked")
      public static String syntax(Class<? extends GenericSetting> genericSettings) {
          Class<Enum<? extends OptionI>> options = (Class<Enum<? extends OptionI>>) genericSettings
                  .getDeclaredClasses()[0];
  
          StringBuilder sb = new StringBuilder();
          StringBuilder sbNotMandetory = new StringBuilder();
  
          if (options == null) {
              String errorMessage = "Driver implementation error, in method syntax(). Could not find class "
                      + genericSettings.getSimpleName() + ". Report driver developer.";
              logger.error(errorMessage);
              sb.append(errorMessage);
          }
          else {
              sb.append("Synopsis:");
              boolean first = true;
              try {
                  Method valueMethod = options.getMethod(PREFIX);
                  Method mandatorylMethod = options.getMethod(MANDATORY);
  
                  for (Enum<? extends OptionI> option : options.getEnumConstants()) {
                      boolean mandatory = (boolean) mandatorylMethod.invoke(option);
                      String value = (String) valueMethod.invoke(option);
                      if (mandatory) {
                          if (!first) {
                              sb.append(SEPARATOR);
                          }
                          first = false;
                          sb.append(' ' + value + PAIR_SEP + " <" + option.name().toLowerCase(LOCALE) + '>');
                      }
                      else {
                          sbNotMandetory.append(
                                  " [" + SEPARATOR + value + PAIR_SEP + " <" + option.name().toLowerCase(LOCALE) + ">]");
                      }
                  }
                  sb.append(sbNotMandetory);
              } catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException
                      | NoSuchMethodException | SecurityException e) {
                  String errorMessage = "Driver implementation error, in method syntax(). Could not find method. Report driver developer.";
                  logger.error(errorMessage);
                  sb.append(errorMessage);
              }
          }
          return sb.toString();
      }
  
      public interface OptionI {
  
          String prefix();
  
          Class<?> type();
  
          boolean mandatory();
      }
  
      /**
       * Example Option Enum
       */
      @SuppressWarnings("unused")
      private enum Option implements OptionI {
          EXAMPLE0("ex0", Integer.class, false),
          EXAMPLE1("ex1", String.class, true);
  
          private final String prefix;
          private final Class<?> type;
          private final boolean mandatory;
  
          private Option(String prefix, Class<?> type, boolean mandatory) {
              this.prefix = prefix;
              this.type = type;
              this.mandatory = mandatory;
          }
  
          @Override
          public String prefix() {
              return this.prefix;
          }
  
          @Override
          public Class<?> type() {
              return this.type;
          }
  
          @Override
          public boolean mandatory() {
              return this.mandatory;
          }
      }
  
      synchronized int parseFields(String settings, Class<? extends Enum<? extends OptionI>> options)
              throws ArgumentSyntaxException {
          String enclosingClassName = options.getEnclosingClass().getSimpleName();
          int enumValuesLength = options.getEnumConstants().length;
  
          Method prefixMethod;
          Method typeMethod;
          Method mandatorylMethod;
  
          String[] settingsArray = settings.trim().split(SEPARATOR);
          int settingsArrayLength = settingsArray.length;
  
          if (settingsArrayLength >= 1 && settingsArrayLength <= enumValuesLength) {
              try {
                  prefixMethod = options.getMethod(PREFIX);
                  typeMethod = options.getMethod(TYPE);
                  mandatorylMethod = options.getMethod(MANDATORY);
              } catch (NoSuchMethodException | SecurityException e) {
                  throw new ArgumentSyntaxException("Driver implementation error, \'" + enclosingClassName
                          + "\' problem to find method in implementation. Report driver developer.\n" + e);
              }
  
              try {
                  for (Enum<? extends OptionI> option : options.getEnumConstants()) {
                      String prefix = (String) prefixMethod.invoke(option);
                      Class<?> type = (Class<?>) typeMethod.invoke(option);
                      boolean mandatory = (boolean) mandatorylMethod.invoke(option);
                      boolean noOptionsPresent = true;
                      String setting = "";
  
                      for (String singlesetting : settingsArray) {
                          setting = singlesetting.trim();
                          String[] pair = setting.split(PAIR_SEP);
                          int pairLength = pair.length;
  
                          if (mandatory && pairLength != 2) {
                              throw new ArgumentSyntaxException("Parameter in " + enclosingClassName
                                      + " is not a pair of prefix and value: <prefix>" + PAIR_SEP + "<value> ");
                          }
                          if (pairLength == 2 && pair[0].trim().equals(prefix)) {
                              try {
                                  noOptionsPresent = false;
                                  setField(pair[1], option.name(), type, options);
                              } catch (NoSuchFieldException | IllegalAccessException e) {
                                  throw new ArgumentSyntaxException("Driver implementation error, \'" + enclosingClassName
                                          + "\' has no corresponding field for parameter " + setting
                                          + ". Report driver developer.\n" + e);
                              }
                          }
                      }
                      if (noOptionsPresent && mandatory) {
                          throw new ArgumentSyntaxException("Mandatory parameter " + option.name() + " is nor present in "
                                  + this.getClass().getSimpleName());
                      }
                  }
              } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
                  throw new ArgumentSyntaxException(
                          "Driver implementation error, \'" + options.getName().toLowerCase(LOCALE)
                                  + "\' problem to invoke method. Report driver developer.\n" + e);
              }
  
          }
          else if (settingsArrayLength > enumValuesLength) {
              throw new ArgumentSyntaxException("Too much parameters in " + enclosingClassName + ".");
          }
          return settingsArrayLength;
      }
  
      private synchronized void setField(String value, String enumName, Class<?> type,
              Class<? extends Enum<? extends OptionI>> options)
              throws IllegalAccessException, NoSuchFieldException, ArgumentSyntaxException {
          String optionName = enumName.toLowerCase(LOCALE);
          value = value.trim();
  
          switch (type.getSimpleName()) {
          case "Boolean":
              options.getDeclaringClass().getDeclaredField(optionName).setBoolean(this, extractBoolean(value, enumName));
              break;
          case "Short":
              options.getDeclaringClass().getDeclaredField(optionName).setShort(this, extractShort(value, enumName));
              break;
          case "Integer":
              options.getDeclaringClass().getDeclaredField(optionName).setInt(this, extractInteger(value, enumName));
              break;
          case "Long":
              options.getDeclaringClass().getDeclaredField(optionName).setLong(this, extractLong(value, enumName));
              break;
          case "Float":
              options.getDeclaringClass().getDeclaredField(optionName).setFloat(this, extractFloat(value, enumName));
              break;
          case "Double":
              options.getDeclaringClass().getDeclaredField(optionName).setDouble(this, extractDouble(value, enumName));
              break;
          case "String":
              options.getDeclaringClass().getDeclaredField(optionName).set(this, value);
              break;
          case "byte[]":
              options.getDeclaringClass().getDeclaredField(optionName).set(this, extractByteArray(value, enumName));
              break;
          case "InetAddress":
              options.getDeclaringClass().getDeclaredField(optionName).set(this, extractInetAddress(value, enumName));
              break;
          default:
              throw new NoSuchFieldException(type.getSimpleName() + "  Driver implementation error, \'"
                      + enumName.toLowerCase(LOCALE) + "\' not supported data type. Report driver developer\n");
          }
      }
  
      private synchronized boolean extractBoolean(String value, String errorMessage) throws ArgumentSyntaxException {
          Boolean ret = false;
          try {
              ret = Boolean.getBoolean(value);
          } catch (NumberFormatException e) {
              argumentSyntaxException(errorMessage, ret.getClass().getSimpleName());
          }
          return ret;
      }
  
      private synchronized short extractShort(String value, String errorMessage) throws ArgumentSyntaxException {
          Short ret = 0;
          try {
              ret = Short.decode(value);
          } catch (NumberFormatException e) {
              argumentSyntaxException(errorMessage, ret.getClass().getSimpleName());
          }
          return ret;
      }
  
      private synchronized int extractInteger(String value, String errorMessage) throws ArgumentSyntaxException {
          Integer ret = 0;
          try {
              ret = Integer.decode(value);
          } catch (NumberFormatException e) {
              argumentSyntaxException(errorMessage, ret.getClass().getSimpleName());
          }
          return ret;
      }
  
      private synchronized long extractLong(String value, String errorMessage) throws ArgumentSyntaxException {
          Long ret = 0l;
          try {
              ret = Long.decode(value);
          } catch (NumberFormatException e) {
              argumentSyntaxException(errorMessage, ret.getClass().getSimpleName());
          }
          return ret;
      }
  
      private synchronized float extractFloat(String value, String errorMessage) throws ArgumentSyntaxException {
          Float ret = 0f;
          try {
              ret = Float.parseFloat(value);
          } catch (NumberFormatException e) {
              argumentSyntaxException(errorMessage, ret.getClass().getSimpleName());
          }
          return ret;
      }
  
      private synchronized double extractDouble(String value, String errorMessage) throws ArgumentSyntaxException {
          Double ret = 0.;
          try {
              ret = Double.parseDouble(value);
          } catch (NumberFormatException e) {
              argumentSyntaxException(errorMessage, ret.getClass().getSimpleName());
          }
          return ret;
      }
  
      private synchronized byte[] extractByteArray(String value, String errorMessage) throws ArgumentSyntaxException {
          byte[] ret = {};
          if (value.startsWith("0x")) {
              try {
                  ret = fromShortHexString(value);
              } catch (NumberFormatException e) {
                  argumentSyntaxException(errorMessage, ret.getClass().getSimpleName());
              }
          }
          else {
              ret = value.getBytes(StandardCharsets.US_ASCII);
          }
  
          return ret;
      }
  
      private synchronized InetAddress extractInetAddress(String value, String errorMessage)
              throws ArgumentSyntaxException {
          InetAddress ret = null;
          try {
              ret = InetAddress.getByName(value);
          } catch (UnknownHostException e) {
              argumentSyntaxException(errorMessage, "InetAddress");
          }
          return ret;
      }
  
      private synchronized void argumentSyntaxException(String errorMessage, String returnType)
              throws ArgumentSyntaxException {
          throw new ArgumentSyntaxException(MessageFormat.format("Value of {0} in {1} is not type of {2}.", errorMessage,
                  this.getClass().getSimpleName(), returnType));
      }
  
      private static byte[] fromShortHexString(String shortHexString) throws NumberFormatException {
  
          validate(shortHexString);
  
  