code: @NotNull List<Object> propertyValues(@NotNull String propertyName, @Nullable Collection<?> target) {
      if (isNullOrEmpty(target)) {
        return emptyList();
      }
      // ignore null elements as we can't extract a property from a null object
      Collection<?> nonNullElements = nonNullElementsIn(target);
      if (isNestedProperty(propertyName)) {
        String firstProperty = firstPropertyIfNested(propertyName);
        List<Object> firstPropertyValues = propertyValues(firstProperty, nonNullElements);
        // extract next sub property values until reaching a last sub property
        return propertyValues(removeFirstPropertyIfNested(propertyName), firstPropertyValues);
      }
      return simplePropertyValues(propertyName, nonNullElements);
    }
docs: Returns a list containing the values of the given property name, from the elements of the given collection. If the
  given collection is empty or {@code null}, this method will return an empty collection.
  <p/>
  For example, given the nested property "address.street.number", this method will:
  <ol>
  <li>extract a collection of "address" from the given collection (remaining property is 'street.number')</li>
  <li>extract a collection of "street" from the "address" collection (remaining property is 'number')</li>
  <li>extract a collection of "number" from the "street" collection</li>
  </ol>
  
  @param propertyName the name of the property. It may be a nested property.
  @param target       the given collection.
  @return a list containing the values of the given property name, from the elements of the given collection.
  @throws NullPointerException if given property name is {@code null}.
  @throws IntrospectionError   if an element in the given collection does not have a matching property.