Fork me on GitHub

Maven Central Build Status Test Status Coverage

Introduction

This project is a collection of miscellaneous tools shared by a number of projects of the same author. There are simple tuples to use with Java Streams, type-safe maps (inspired by the heterogeneous map pattern) described in Effective Java by Joshua Bloch, a finder that handles in a smart way queries to data sources, a facility to use the DCI (Data, Context and Interactions) architectural pattern, a simple message bus suitable for using the pub-sub pattern inside an application, some test utilities, an experimental actor framework and a few other small things.

Yes, the project name is a tribute to the jazz standard with the same name by Maschwitz and Strachey.

The project is composed of mostly independent modules, with different levels of stability and internal quality. At the moment there is not an automatic system to check the compatibility of the APIs from version to version; in general look at the following Javadoc tags:

  • stable: to designate things with a certain degree of maturity.
  • draft: to designate things that are going to stabilise;
  • experimental: to designate things that are really raw and might go away soon, or perhaps mutate dramatically;

TheseFoolishThings is licensed with the Apache license.

Table of contents

A quick look

Here it is a quick look at some of the available features.

Pair and Triple

Pair and Triple are implementations of 2 and 3-elements tuples. They are pretty useful in Stream programming, for instance offering a way to implement nested loops:

final var actual = IntStream.rangeClosed(1, limit)
                            .boxed()
                            .flatMap(a -> Pair.pairRangeClosed(a, a + 1, limit))
                            .flatMap(p -> Triple.tripleRangeClosed(p, p.b + 1, limit))
                            .collect(toList());

… or indexed loops:

final var actual =  IntStream.rangeClosed(1, limit)
                             .boxed()
                             .flatMap(a -> Pair.pairRangeClosed(a, a + 1, limit))
                             .collect(toList());

It is also possible to “zip” two streams into a Stream<Pair>:

// given
final var intStream = IntStream.range(0, 5).boxed();
final var stringStream = IntStream.range(0, 5).mapToObj(n -> "string-" + (char)('a' + n));
// when
final var underTest = Pair.zip(intStream, stringStream);
// then
assertThat(underTest.collect(toList()), is(asList(Pair.of(0, "string-a"),
                                                  Pair.of(1, "string-b"),
                                                  Pair.of(2, "string-c"),
                                                  Pair.of(3, "string-d"),
                                                  Pair.of(4, "string-e"))));

Find more information in the documentation for module Utilities.

Finder

Finder is a query builder with a fluent API that allows to access collections of data from a variety of data sources. It offers features such as pagination and composition. Its API is extensible, so a variety of filtering criteria and query parameters can be specified. Notwithstanding the apparent similarity with Streams, it is quite different since it supports not only in-memory operations; for instance, implementations can query a database by generating ad-hoc SQL, so data are retrieved in the most efficient way.

        log.info("Whose first name starts with B: {}",
                 registry.findPerson()
                         .withFirstName("B.*")
                         .results());

        log.info("Whose first name starts with B, sorted by first name: {}",
                 registry.findPerson()
                         .sort(BY_FIRST_NAME)
                         .withFirstName("B.*")
                         .results());

Find more information in the documentation for module Utilities.

As

As provides a dynamic implementation of the Data, Context and Interaction architectural pattern — dynamic in the sense that classes providing interaction support can be associated to existing objects. For instance, in the following code Displayable and Renderable are not known to the model class Person, but they are available at runtime in association with that class to provide extended functions:

@ExtensionMethod(AsExtensions.class) @Slf4j
public class DisplayableExample
  {
    public void run()
      {
        final var joe = new Person(new Id("1"), "Joe", "Smith");
        final var luke = new Person(new Id("2"), "Luke", "Skywalker");
        // approach with classic getter
        log.info("******** (joe as Displayable).displayName: {}", joe.as(_Displayable_).getDisplayName());
        log.info("******** (luke as Displayable).displayName: {}", luke.as(_Displayable_).getDisplayName());
        // approach oriented to Tell Don't Ask
        joe.as(_Renderable_).renderTo("******** (joe as Renderable): %1$s %2$s ", log::info);
        luke.as(_Renderable_).renderTo("******** (luke as Renderable): %1$s %2$s ", log::info);
      }
  }

Find more information in the documentation for module Utilities.

P&S MessageBus

PENDING INTRODUCTION AND EXAMPLE

Find more information in the documentation for module MessageBus.

Collaborative Actors

PENDING INTRODUCTION AND EXAMPLE

Find more information in the documentation for module Actors.

Type-safe maps

Inspired by the heterogeneous map pattern described in Effective Java by Joshua Bloch, TypeSafeMap is an immutable map that works with type-aware keys, so its retrieval method is type-safe; furthermore it supports Optional.

final Key<String> k1 = Key.of("Key 1", String.class);
final Key<Integer> k2 = Key.of("Key 2", Integer.class);
final var m = TypeSafeMap.newInstance()
                         .with(k1, "Value 1")
                         .with(k2, 1);
final Optional<String> v1 = m.getOptional(k1);
final Optional<Integer> v2 = m.getOptional(k2);
assertThat(v1.get(), is("Value 1"));
assertThat(v2.get(), is(1));

TypeSafeMultiMap is similar, but associates keys to collection of values, not single values; so associating multiple (key, value) pairs keep all the values instead of replacing the previous one.

final Key<String> k1 = Key.of("Key 1", String.class);
final Key<Integer> k2 = Key.of("Key 2", Integer.class);
final var m = TypeSafeMultiMap.newInstance()
                              .with(k1, "Value 1")
                              .with(k1, "Value 2")
                              .with(k2, 1)
                              .with(k2, 2);
final Collection<String> v1 = m.get(k1);
final Collection<Integer> v2 = m.get(k2);
assertThat(v1, is(List.of("Value 1", "Value 2")));
assertThat(v2, is(List.of(1, 2)));

Find more information in the documentation for module Utilities.

Modules

General information

Maven dependency

Modules can be used independently, so each needed one must be included separately. The master POM of the project can be used as a BOM and included in the dependencyManagement section:

<dependencyManagement>
    <dependencies>
        ...
        <dependency>
            <groupId>it.tidalwave.thesefoolishthings</groupId>
            <artifactId>thesefoolishthings</artifactId>
            <version>3.2-ALPHA-19</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        ...
    </dependencies>
</dependencyManagement>

Sources, issue tracker and continuous integration

The primary source repository is on Bitbucket, a secondary repository (synchronized in real time) is available on GitHub.

To checkout sources from Bitbucket:

> git clone https://bitbucket.org/tidalwave/thesefoolishthings-src

To checkout sources from GitHub:

> git clone https://github.com/tidalwave-it/thesefoolishthings-src

The issue tracker is hosted on the Atlassian Jira Cloud:

The continuous integration is available at:

API documentation

Aggregate Javadoc