docs: public static <T> T createInternal(Config config, Class<T> clazz) {
          if (((SimpleConfig)config).root().resolveStatus() != ResolveStatus.RESOLVED)
              throw new ConfigException.NotResolved(
                      "need to Config#resolve() a config before using it to initialize a bean, see the API docs for Config#resolve()");
  
          Map<String, AbstractConfigValue> configProps = new HashMap<String, AbstractConfigValue>();
          Map<String, String> originalNames = new HashMap<String, String>();
          for (Map.Entry<String, ConfigValue> configProp : config.root().entrySet()) {
              String originalName = configProp.getKey();
              String camelName = ConfigImplUtil.toCamelCase(originalName);
              // if a setting is in there both as some hyphen name and the camel name,
              // the camel one wins
              if (originalNames.containsKey(camelName) && !originalName.equals(camelName)) {
                  // if we aren't a camel name to start with, we lose.
                  // if we are or we are the first matching key, we win.
              } else {
                  configProps.put(camelName, (AbstractConfigValue) configProp.getValue());
                  originalNames.put(camelName, originalName);
              }
          }
  
          BeanInfo beanInfo = null;
          try {
              beanInfo = Introspector.getBeanInfo(clazz);
          } catch (IntrospectionException e) {
              throw new ConfigException.BadBean("Could not get bean information for class " + clazz.getName(), e);
          }
  
          try {
              List<PropertyDescriptor> beanProps = new ArrayList<PropertyDescriptor>();
              for (PropertyDescriptor beanProp : beanInfo.getPropertyDescriptors()) {
                  if (beanProp.getReadMethod() == null || beanProp.getWriteMethod() == null) {
                      continue;
                  }
                  beanProps.add(beanProp);
              }
  
              // Try to throw all validation issues at once (this does not comprehensively
              // find every issue, but it should find common ones).
              List<ConfigException.ValidationProblem> problems = new ArrayList<ConfigException.ValidationProblem>();
              for (PropertyDescriptor beanProp : beanProps) {
                  Method setter = beanProp.getWriteMethod();
                  Class<?> parameterClass = setter.getParameterTypes()[0];
  
                  ConfigValueType expectedType = getValueTypeOrNull(parameterClass);
                  if (expectedType != null) {
                      String name = originalNames.get(beanProp.getName());
                      if (name == null)
                          name = beanProp.getName();
                      Path path = Path.newKey(name);
                      AbstractConfigValue configValue = configProps.get(beanProp.getName());
                      if (configValue != null) {
                          SimpleConfig.checkValid(path, expectedType, configValue, problems);
                      } else {
                          if (!isOptionalProperty(clazz, beanProp)) {
                              SimpleConfig.addMissing(problems, expectedType, path, config.origin());
                          }
                      }
                  }
              }
  
              if (!problems.isEmpty()) {
                  throw new ConfigException.ValidationFailed(problems);
              }
  
              // Fill in the bean instance
              T bean = clazz.newInstance();
              for (PropertyDescriptor beanProp : beanProps) {
                  Method setter = beanProp.getWriteMethod();
                  Type parameterType = setter.getGenericParameterTypes()[0];
                  Class<?> parameterClass = setter.getParameterTypes()[0];
                  String configPropName = originalNames.get(beanProp.getName());
                  // Is the property key missing in the config?
                  if (configPropName == null) {
                      // If so, continue if the field is marked as @{link Optional}
                      if (isOptionalProperty(clazz, beanProp)) {
                          continue;
                      }
                      // Otherwise, raise a {@link Missing} exception right here
                      throw new ConfigException.Missing(beanProp.getName());
                  }
                  Object unwrapped = getValue(clazz, parameterType, parameterClass, config, configPropName);
                  setter.invoke(bean, unwrapped);
              }
              return bean;
          } catch (InstantiationException e) {
              throw new ConfigException.BadBean(clazz.getName() + " needs a public no-args constructor to be used as a bean", e);
          } catch (IllegalAccessException e) {
              throw new ConfigException.BadBean(clazz.getName() + " getters and setters are not accessible, they must be for use as a bean", e);
          } catch (InvocationTargetException e) {
              throw new ConfigException.BadBean("Calling bean method on " + clazz.getName() + " caused an exception", e);
          }
      }