code: ///////////////////////////////////////////////////////////////////////////////////////////////
  // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
  // Copyright (C) 2001-2023 the original author or authors.
  //
  // This library is free software; you can redistribute it and/or
  // modify it under the terms of the GNU Lesser General Public
  // License as published by the Free Software Foundation; either
  // version 2.1 of the License, or (at your option) any later version.
  //
  // This library 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
  // Lesser General Public License for more details.
  //
  // You should have received a copy of the GNU Lesser General Public
  // License along with this library; if not, write to the Free Software
  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  ///////////////////////////////////////////////////////////////////////////////////////////////
  
  package com.puppycrawl.tools.checkstyle;
  
  import static com.google.common.truth.Truth.assertWithMessage;
  
  import java.net.URI;
  import java.util.Arrays;
  import java.util.regex.Pattern;
  
  import org.apache.commons.beanutils.ConversionException;
  import org.apache.commons.beanutils.ConvertUtilsBean;
  import org.apache.commons.beanutils.Converter;
  import org.junit.jupiter.api.Test;
  
  import com.puppycrawl.tools.checkstyle.api.CheckstyleException;
  import com.puppycrawl.tools.checkstyle.api.Scope;
  import com.puppycrawl.tools.checkstyle.api.SeverityLevel;
  import com.puppycrawl.tools.checkstyle.checks.naming.AccessModifierOption;
  import com.puppycrawl.tools.checkstyle.internal.utils.TestUtil;
  
  public class AbstractAutomaticBeanTest {
      @Test
      public void testConfigureNoSuchAttribute() {
          final TestBean testBean = new TestBean();
          final DefaultConfiguration conf = new DefaultConfiguration("testConf");
          conf.addProperty("NonExistent", "doesn't matter");
          try {
              testBean.configure(conf);
              assertWithMessage("Exception is expected")
                      .fail();
          }
          catch (CheckstyleException ex) {
              assertWithMessage("Exceptions cause should be null")
                      .that(ex)
                      .hasCauseThat()
                      .isNull();
              assertWithMessage("Invalid exception message")
                      .that(ex)
                      .hasMessageThat()
                      .isEqualTo("Property 'NonExistent' does not exist,"
                              + " please check the documentation");
          }
      }
  
      @Test
      public void testConfigureNoSuchAttribute2() {
          final TestBean testBean = new TestBean();
          final DefaultConfiguration conf = new DefaultConfiguration("testConf");
          conf.addProperty("privateField", "doesn't matter");
          try {
              testBean.configure(conf);
              assertWithMessage("Exception is expected")
                      .fail();
          }
          catch (CheckstyleException ex) {
              assertWithMessage("Exceptions cause should be null")
                      .that(ex)
                      .hasCauseThat()
                      .isNull();
              assertWithMessage("Invalid exception message")
                      .that(ex)
                      .hasMessageThat()
                      .isEqualTo("Property 'privateField' does not exist,"
                              + " please check the documentation");
          }
      }
  
      @Test
      public void testSetupChildFromBaseClass() throws CheckstyleException {
          final TestBean testBean = new TestBean();
          testBean.configure(new DefaultConfiguration("bean config"));
          testBean.setupChild(null);
          try {
              testBean.setupChild(new DefaultConfiguration("dummy"));
              assertWithMessage("Exception is expected")
                      .fail();
          }
          catch (CheckstyleException ex) {
              final String expectedMessage = "dummy is not allowed as a child in bean config. "
                      + "Please review 'Parent Module' section for this Check"
                      + " in web documentation if Check is standard.";
              assertWithMessage("Invalid exception message")
                      .that(ex)
                      .hasMessageThat()
                      .isEqualTo(expectedMessage);
          }
      }
  
      @Test
      public void testSetupInvalidChildFromBaseClass() {
          final TestBean testBean = new TestBean();
          final DefaultConfiguration parentConf = new DefaultConfiguration("parentConf");
          final DefaultConfiguration childConf = new DefaultConfiguration("childConf");
          TestUtil.setInternalState(testBean, "configuration", parentConf);
  
          try {
              testBean.setupChild(childConf);
              assertWithMessage("expecting checkstyle exception")
                      .fail();
          }
          catch (CheckstyleException ex) {
              assertWithMessage("Invalid exception message")
                      .that(ex)
                      .hasMessageThat()
                      .isEqualTo("childConf is not allowed as a "
                              + "child in parentConf. Please review 'Parent Module' section "
                              + "for this Check in web documentation if Check is standard.");
          }
      }
  
      @Test
      public void testContextualizeInvocationTargetException() {
          final TestBean testBean = new TestBean();
          final DefaultContext context = new DefaultContext();
          context.add("exceptionalMethod", 123.0f);
          try {
              testBean.contextualize(context);
              assertWithMessage("InvocationTargetException is expected")
                      .fail();
          }
          catch (CheckstyleException ex) {
              final String expected = "Cannot set property 'exceptionalMethod' to '123.0'";
              assertWithMessage("Invalid exception cause, should be: ReflectiveOperationException")
                      .that(ex)
                      .hasCauseThat()
                      .isInstanceOf(ReflectiveOperationException.class);
              assertWithMessage("Invalid exception message, should start with: " + expected)
                      .that(ex)
                      .hasMessageThat()
                      .isEqualTo(expected);
          }
      }
  
      @Test
      public void testContextualizeConversionException() {
          final TestBean testBean = new TestBean();
          final DefaultContext context = new DefaultContext();
          context.add("val", "some string");
          try {
              testBean.contextualize(context);
              assertWithMessage("InvocationTargetException is expected")
                      .fail();
          }
          catch (CheckstyleException ex) {
              final String expected = "illegal value ";
              assertWithMessage("Invalid exception cause, should be: ConversionException")
                      .that(ex)
                      .hasCauseThat()
                      .isInstanceOf(ConversionException.class);
              assertWithMessage("Invalid exception message, should start with: " + expected)
                      .that(ex)
                      .hasMessageThat()
                      .startsWith(expected);
          }
      }
  
      @Test
      public void testTestBean() {
          final TestBean testBean = new TestBean();
          testBean.setVal(0);
          testBean.setWrong("wrongVal");
          testBean.assignPrivateFieldSecretly(null);
          try {
              testBean.setExceptionalMethod("someValue");
              assertWithMessage("exception expected")
                      .fail();
          }
          catch (IllegalStateException ex) {
              assertWithMessage("Invalid exception message")
                      .that(ex)
                      .hasMessageThat()
                      .isEqualTo("null,wrongVal,0,someValue");
          }
      }
  
      @Test
      public void testRegisterIntegralTypes() throws Exception {
          final ConvertUtilsBeanStub convertUtilsBean = new ConvertUtilsBeanStub();
          TestUtil.invokeStaticMethod(AbstractAutomaticBean.class,
                  "registerIntegralTypes", convertUtilsBean);
          assertWithMessage("Number of converters registered differs from expected")
                  .that(convertUtilsBean.getRegisterCount())
                  .isEqualTo(81);
      }
  
      @Test
      public void testBeanConverters() throws Exception {
          final ConverterBean bean = new ConverterBean();
  
          // methods are not seen as used by reflection
          bean.setStrings("BAD");
          bean.setPattern(null);
          bean.setSeverityLevel(null);
          bean.setScope(null);
          bean.setUri(null);
          bean.setAccessModifiers(AccessModifierOption.PACKAGE);
  
          final DefaultConfiguration config = new DefaultConfiguration("bean");
          config.addProperty("strings", "a, b, c");
          config.addProperty("pattern", ".*");
          config.addProperty("severityLevel", "error");
          config.addProperty("scope", "public");
          config.addProperty("uri", "http://github.com");
          config.addProperty("accessModifiers", "public, private");
          bean.configure(config);
  
          final String message = "invalid result";
          assertWithMessage(message)
                  .that(bean.strings)
                  .asList()
                  .containsExactly("a", "b", "c")
                  .inOrder();
          assertWithMessage(message)
                  .that(bean.pattern.pattern())
                  .isEqualTo(".*");
          assertWithMessage(message)
                  .that(bean.severityLevel)
                  .isEqualTo(SeverityLevel.ERROR);
          assertWithMessage(message)
                  .that(bean.scope)
                  .isEqualTo(Scope.PUBLIC);
          assertWithMessage(message)
                  .that(bean.uri)
                  .isEqualTo(new URI("http://github.com"));
          assertWithMessage(message)
                  .that(bean.accessModifiers)
                  .asList()
                  .containsExactly(AccessModifierOption.PUBLIC, AccessModifierOption.PRIVATE)
                  .inOrder();
      }
  
      @Test
      public void testBeanConvertersUri2() throws Exception {
          final ConverterBean bean = new ConverterBean();
          final DefaultConfiguration config = new DefaultConfiguration("bean");
          config.addProperty("uri", "");
          bean.configure(config);
  
          assertWithMessage("invalid result")
                  .that(bean.uri)
                  .isNull();
      }
  
      @Test
      public void testBeanConvertersUri3() {
          final ConverterBean bean = new ConverterBean();
          final DefaultConfiguration config = new DefaultConfiguration("bean");
          config.addProperty("uri", "BAD");
  
          try {
              bean.configure(config);
              assertWithMessage("Exception is expected")
                      .fail();
          }
          catch (CheckstyleException ex) {
              assertWithMessage("Error message is not expected")
                      .that(ex)
                      .hasMessageThat()
                      .isEqualTo("illegal value 'BAD' for property 'uri'");
          }
      }
  
      private static final class ConvertUtilsBeanStub extends ConvertUtilsBean {
  
          private int registerCount;
  
          @Override
          public void register(Converter converter, Class<?> clazz) {
              super.register(converter, clazz);
              if (converter != null) {
                  registerCount++;
              }
          }
  
          public int getRegisterCount() {
              return registerCount;
          }
  
      }
  
      public static final class TestBean extends AbstractAutomaticBean {
  
          private String privateField;
  
          private String wrong;
  
          private int val;
  
          public void setWrong(String wrong) {
              this.wrong = wrong;
          }
  
          public void setVal(int val) {
              this.val = val;
          }
  
          public void assignPrivateFieldSecretly(String input) {
              privateField = input;
          }
  
          public void setExceptionalMethod(String value) {
              throw new IllegalStateException(privateField + "," + wrong + "," + val + "," + value);
          }
  
          @Override
          protected void finishLocalSetup() {
              // No code by default
          }
  
      }
  
      /**
       * This class has to be public for reflection to access the methods.
       */
      public static class ConverterBean extends AbstractAutomaticBean {
  
          private String[] strings;
          private Pattern pattern;
          private SeverityLevel severityLevel;
          private Scope scope;
          private URI uri;
          private AccessModifierOption[] accessModifiers;
  
          /**
           * Setter for strings.
           *
           * @param strings strings.
           */
          public void setStrings(String... strings) {
              this.strings = Arrays.copyOf(strings, strings.length);
          }
  
          /**
           * Setter for pattern.
           *
           * @param pattern pattern.
           */
          public void setPattern(Pattern pattern) {
              this.pattern = pattern;
          }
  
          /**
           * Setter for severity level.
           *
           * @param severityLevel severity level.
           */
          public void setSeverityLevel(SeverityLevel severityLevel) {
              this.severityLevel = severityLevel;
          }
  
          /**
           * Setter for scope.
           *
           * @param scope scope.
           */
          public void setScope(Scope scope) {
              this.scope = scope;
          }
  
          /**
           * Setter for uri.
           *
           * @param uri uri.
           */
          public void setUri(URI uri) {
              this.uri = uri;
          }
  
          /**
           * Setter for access modifiers.
           *
           * @param accessModifiers access modifiers.
           */
          public void setAccessModifiers(AccessModifierOption... accessModifiers) {
              this.accessModifiers = Arrays.copyOf(accessModifiers,
                      accessModifiers.length);
          }
  
          @Override
          protected void finishLocalSetup() {
              // no code
          }
  
      }
  
  }
  