AndroidLibrary example

AndroidLibrary is an Android application developed with DSL Platform, meaning that it uses DSL files to define the domain model, which will in turn be compiled to create the structure and the relations of database entities, source files, etc. The application displays books (which you can search) and reviews related to the book. Besides the book and reviews there is a option to display the number of books from an author. The books are based on Amazon, and we want to have some basic information on books like Amazon does. For example, take a look at the picture below:

This tutorial will guide you how to create an app on your own and how to use DSL generated model. It is also available for download on GitHub. Each part corresponds to one tagged release on GitHub and can be downloaded separately. You can use the commands listed at the beginning of each part to get it.

0. Part – v0.1 – plain Android project from dsl-platform.com

[git checkout v0.1]

This release is just the default Android project you have when you create and download an Android project from dsl-platform.com.

Here is it important to learn how to initialize your ServiceLocator so you can communicate with your model. This is already done for you in the downloaded template, inside the MainActivity as the init() method:

/**
 * Call to initialize an instance of ServiceLocator with a dsl-project.props
 */
public static ServiceLocator init() throws IOException {
    final ServiceLocator locator = Bootstrap.init(MainActivity.class
            .getResourceAsStream("/dsl-project.props"));

    // an example how to resolve components from the ServiceLocator
    locator.resolve(Logger.class).info("Locator has been initialized.");
    return locator;
}

The method uses the information from the dsl-project.props file that is generated when creating a project on DSL Platform.

1. Part – v1.0 – listing books

[git checkout v1.0]

In this part you will see how you can fetch the data from database and display it in your Android application. Here is the DSL code we are using in this part:

module AndroidLibrary
{
  aggregate Book(isbn) {
    String(14)    isbn; // "978-0590353427"
    String        title;
    List<String>  authors;
    List<Review>  reviews;
    String        publisher;
    String        language;
    Date          publishedOn;
    Int           pages;
  }

  entity Review {
    int     rating;
    String  comment;
    String  reviewedBy;
  }
}
  • aggregate Book

    is the aggregate root concept which holds information about a book. You can see that we have strings for the title, isbn, publisher and language, we have date for the date of publishing, a integer (number) for the number of pages and we have two lists. The first list is a list of strings containing authors, because there can be more than one author for a book. The second list is a list of Reviews. These are reviews that readers wrote for this book.

  • entity Review

    is a entity which consists of two string properties (for comment and the name of the user who wrote it) and one integer property (for the rating).

Entity is a mutable object with identity closely connected to the aggregate root, while aggregate root is an entity which encapsulates multiple entities and values to create a single cohesive object.

Save this DSL in your dsl folder as AndroidLibrary.dsl file name. Then go to command line and run compile.sh or compile.bat if you are on Windows. Enter Y to all prompts and if everything is OK, new Java sources will be generated in src/generated/java folder. This is something you need to do every time you change your dsl file, so the changes are updated on the domain model too.

To make things a bit easier, we created for you a simple method that populates the database with mockup data. You can download the java file here and add it to your project, or if you are using our git project it is located in src/test/java/DatabaseTest.java. Please execute it as an Android JUnit test. This will delete all data in the database and insert mockup data that we can use in this tutorial.

Now that we have established communication with our model, compiled the DSL files and populated our database with some data; we can try to fetch that data from the database. The next code snippet shows how you can get all objects of one class, in this case all Book objects.

    List<Book> bookList = Book.search();

Note that search() method can be used only on aggregate root elements like Book, but not on entity elements like our Review. Therefore a Review information should be accessed only through a Book. For further information about aggregate root and entity visit .

After we got the list of all books we forward the list to the BookArrayAdapter who represents them for our listView so he can display them. Now we have a listView with all our books, just like the picture bellow:

2. Part - v2.0 – search books specification

[git checkout v2.0]

The next feature our AndroidLibrary has is to search our books, for that purpose we are going to use specifications. Specification is a predicate which states a condition and can be used for defining search conditions, validations etc.. Lets see how the search specification in our aggregate root Book looks like:

    specification searchBooks 'b =>
        b.isbn.Contains(pattern) || 
        b.title.ToLower().Contains(pattern) || 
        b.authors.Any(a => a.ToLower().Contains(pattern))'
    {
      String pattern;
    }

searchBooks is the name of our specification, and the pattern is the attribute we will send to the specification. The expression is going to use the pattern to compare it with the isbn, title or authors name. To write the expression you need to use the C# LINQ syntax, e.g.:

    b.title.ToLower().Contains(pattern)

which means: get the title of the book, use ToLower() method to change strings to be all lowercase and than use the Contains(String value) method to see if the pattern substring occurs within the title.

And now the code snippet for using the Specification in our SearchActivity:

    final BookRepository bookRepository = new BookRepository(MainActivity.locator);
    final Specification<Book> searchBooks = new Book.searchBooks(query);
    final Future<List<Book>> futureResults = bookRepository.search(searchBooks);

    List<Book> bookList = futureResults.get();

With the first line we are creating a BookRepository which we use to avoid blocking calls. Than we create a searchBooks specification with query (String) we got from the SearchView, after which we can pass the searchBooks specification to the bookRepository as an argument of search method. It will return Future<List>, so we can access the List with a simple get() method like the last line of the code snippet is suggesting.

At the end we have a list of books who meet the specification's query, which we are displaying in our SearchActivity.

3. Part – v2.1 – displaying reviews

[git checkout v2.1]

Beides the searchBooks specification we have another one for finding a book with the corresponded ISBN:

    specification isbnEquals 'b => b.isbn.Equals(isbn)' {
        String isbn;
    }

isbnEquals is the name of the specification and it searches all books whose property isbn is equal to the String isbn. It is a very simple specification which we use to get the book for which we want to get all reviews. The code for using the specification in the Activity is the same for all specifications:

    //specification for getting the book
    final BookRepository bookRepository = new BookRepository(locator);
    final Specification<Book> isbnEquals = new Book.isbnEquals(isbn);
    final Future<List<Book>> futureResults = bookRepository.search(isbnEquals);

But there is a difference between this specification and the previous one in the way they use the data from the Future list. When we use this specification we only need the first element from the list, not the whole list. Because we have only one book with one ISBN (isbn is the primary key, the unique identifier), so it could not be more elements in the list. So this is how your for-each loop would look like for getting all reviews for a book:

    for (final Review r : futureResults.get().get(0).getReviews()) {
  // ...
    }

After adding data to all lists required for the ListDialogFragment (DialogFragment for displaying a list with reviews) we can create and show it.

4. Part – v3.0 – persisting data to database

[git checkout v3.0]

As you could see I showed you how you can get data from your database but I didn't mentioned anything about adding data/objects to database. To add data to your database you need to use the create() method.

    final Book book = new Book();
    book.create()

This is the example how you persist a new aggregate root object, but if you want to add some entity object you need to get the aggregate object the entity is linked with add data to it and than persist the root. That's what we needed to do in our case, for adding a Review (entity).

Here we are using the isbnEquals specification for getting the book so we can afterwards add new reviews to the existing list of reviews.

Than we created a new review based on the user's input in the dialog:

    //creating new Review that needs to be added
    final Review review = new Review();
    review.setRating(Integer.parseInt(String.valueOf(spinnerRating.getSelectedItem())));
    review.setReviewdBy(editReviewedBy.getText().toString());
    review.setComment(editComment.getText().toString());

And at least getting the book, adding the review and persisting the book:

    //getting the book, adding the review and updating it
    Book book = futureResults.get().get(0);
    book.getReviews().add(review);
    book.update();

If you want more examples of how to persist data to the database you can look how out DatabaseTest class in the src/test/java folder looks like. Because there we first delete all books, and than add some mockup data so you could use them in the app.

4. Part – v4.0 – reporting

[git checkout v4.0]

Only one more thing is left in this tutorial, and that is the report. In our case the report is returning the number of books for an author. First let's see how the report looks in our DSL file:

    report AuthorsBookCount {
      String author;
        count<Book> BookCount 'b => b.authors.Any(a => a.Equals(author))';
    }

To get the number of books we used the report count property which increases the counter if the expression is true. As you can see we are going through our list of authors in the Book, and comparing if the author from the list equals the author which is given to the report (String author). The parameter in the count property is Book, because we are searching number of books (that means he is counting books).

Lets get to the MainActivity activity to see how we create an report:

    AuthorsBookCount count = new AuthorsBookCount();

Than we set the author String with the following (the String s represents a String object from the list of strings we passed to the method):

    count.setAuthor(s);

After that is done we need to get the AuthorsBookCount with the populate(ServiceLocator locator) method from which we can get the result for BookCount with the get method. It's going to be clearer with the code snippet:

    authorsBookCountMap.put(s, count.populate(locator).getBookCount());

The authorsBookCountMap is a HashMap in which we hold all the book counts for all authors:

    HashMap<String, Long> authorsBookCountMap = new HashMap<String, Long>();

That's it - hope you found this brief tutorial helpful! There will soon be more tutorials of all kinds posted on DSL Platform's GitHub pages.