/**
   * Generate the ChangeMetaData for this class. Default implementation reads from the @{@link
   * DatabaseChange } annotation and calls out to {@link #createChangeParameterMetadata(String)} for
   * each property.
   *
   * @throws UnexpectedLiquibaseException if no @DatabaseChange annotation on this Change class
   */
  @Override
  public ChangeMetaData createChangeMetaData() {
    try {
      DatabaseChange databaseChange = this.getClass().getAnnotation(DatabaseChange.class);

      if (databaseChange == null) {
        throw new UnexpectedLiquibaseException(
            "No @DatabaseChange annotation for " + getClass().getName());
      }

      Set<ChangeParameterMetaData> params = new HashSet<ChangeParameterMetaData>();
      for (PropertyDescriptor property :
          Introspector.getBeanInfo(this.getClass()).getPropertyDescriptors()) {
        if (isInvalidProperty(property)) {
          continue;
        }
        Method readMethod = property.getReadMethod();
        Method writeMethod = property.getWriteMethod();
        if (readMethod == null) {
          try {
            readMethod =
                this.getClass().getMethod("is" + StringUtils.upperCaseFirst(property.getName()));
          } catch (Exception ignore) {
            // it was worth a try
          }
        }
        if (readMethod != null && writeMethod != null) {
          DatabaseChangeProperty annotation =
              readMethod.getAnnotation(DatabaseChangeProperty.class);
          if (annotation == null || annotation.isChangeProperty()) {
            params.add(createChangeParameterMetadata(property.getDisplayName()));
          }
        }
      }

      Map<String, String> notes = new HashMap<String, String>();
      for (DatabaseChangeNote note : databaseChange.databaseNotes()) {
        notes.put(note.database(), note.notes());
      }

      return new ChangeMetaData(
          databaseChange.name(),
          databaseChange.description(),
          databaseChange.priority(),
          databaseChange.appliesTo(),
          notes,
          params);
    } catch (Throwable e) {
      throw new UnexpectedLiquibaseException(e);
    }
  }
 /**
  * Create the {@link ChangeParameterMetaData} "serializationType" value. Uses the value on the
  * DatabaseChangeProperty annotation or returns {@link SerializationType}.NAMED_FIELD as a
  * default.
  */
 @SuppressWarnings("UnusedParameters")
 protected SerializationType createSerializationTypeMetaData(
     String parameterName, DatabaseChangeProperty changePropertyAnnotation) {
   if (changePropertyAnnotation == null) {
     return SerializationType.NAMED_FIELD;
   }
   return changePropertyAnnotation.serializationType();
 }
 /**
  * Create the {@link ChangeParameterMetaData} "description" value. Uses the value on the
  * DatabaseChangeProperty annotation or returns null as a default.
  */
 @SuppressWarnings("UnusedParameters")
 protected String createDescriptionMetaData(
     String parameterName, DatabaseChangeProperty changePropertyAnnotation) {
   if (changePropertyAnnotation == null) {
     return null;
   }
   return StringUtils.trimToNull(changePropertyAnnotation.description());
 }
 /**
  * Create the {@link ChangeParameterMetaData} "supportedDatabase" value. Uses the value on the
  * DatabaseChangeProperty annotation or returns an array containing the string "COMPUTE" as a
  * default. "COMPUTE" will cause ChangeParameterMetaData to attempt to determine the required
  * databases based on the generated Statements
  */
 @SuppressWarnings("UnusedParameters")
 protected String[] createSupportedDatabasesMetaData(
     String parameterName, DatabaseChangeProperty changePropertyAnnotation) {
   if (changePropertyAnnotation == null) {
     return new String[] {ChangeParameterMetaData.COMPUTE};
   } else {
     return changePropertyAnnotation.supportsDatabase();
   }
 }
  /**
   * Create the {@link ChangeParameterMetaData} "mustEqual" value. Uses the value on the
   * DatabaseChangeProperty annotation or returns null as a default.
   */
  @SuppressWarnings("UnusedParameters")
  protected String createMustEqualExistingMetaData(
      String parameterName, DatabaseChangeProperty changePropertyAnnotation) {
    if (changePropertyAnnotation == null) {
      return null;
    }

    return changePropertyAnnotation.mustEqualExisting();
  }
  /**
   * Create the {@link ChangeParameterMetaData} "example" value. Uses the value on the
   * DatabaseChangeProperty annotation or returns null as a default. Returns map with key=database
   * short name, value=example. Use short-name "all" as the fallback.
   */
  @SuppressWarnings("UnusedParameters")
  protected Map<String, Object> createExampleValueMetaData(
      String parameterName, DatabaseChangeProperty changePropertyAnnotation) {
    if (changePropertyAnnotation == null) {
      return null;
    }

    Map<String, Object> examples = new HashMap<String, Object>();
    examples.put("all", StringUtils.trimToNull(changePropertyAnnotation.exampleValue()));

    return examples;
  }