3. Exploiting Derived Inverse Reference Properties in the User Interface

We can now exploit the derived inverse reference properties Publisher::publishedBooks and Author::authoredBooks for more efficiently creating a list of associated books in the list publishers and list authors use cases.

3.1. Show information about published books in the List Publishers use case

For showing information about published books in the list publishers use case, we can now exploit the derived inverse reference property publishedBooks:

<ui:composition template="/WEB-INF/templates/page.xhtml">
  <ui:define name="content">
    <h:dataTable value="#{publisherController.publishers}" var="p">
      ...
      <h:column>
        <f:facet name="header">Published books</f:facet>
        <h:outputText value="#{p.publishedBooks}" escape="false" converter="pl.model.converter.BookListConverter"/>
      </h:column>
    </h:dataTable>
    <h:button value="Back" outcome="index" />
  </ui:define>
</ui:composition>

In the case of unidirectional associations, for the case of list books use case, we have used a method in the Book model class, i.e., getAuthorNames(), which returns a text containing the comma separed author names of the book:

public class Book {
  ...
  public String getAuthorNames() {
    String result = "";
    int i = 0, n = 0;
    if ( this.authors != null) {
      n = this.authors.size();
      for ( Author author : this.authors) {
        result += author.getName();
        if ( i < n - 1) {
          result += ", ";
        }
        i++;
      }
    }
    return result;
  } 
}

This makes sense in the case of a book, since the number of authors is in general limited to a small number. However, a Publisher may have a large number of published books. As a better alternative to our string serialization, we can use a JSF converter class which allows us to present the list of authors in a custom way. In our case, we choose to present it as list of names, where every name is presented on a separat line. For this, we implement the pl.model.converter.BookListConverter class and we annotate it with @FacesConverter as follows:

@FacesConverter( value = "pl.model.converter.BookListConverter")
public class BookListConverter implements Converter {
  @Override
  public Object getAsObject( FacesContext context, UIComponent component, String value) {
    // no need for mapping to object...
    return null;
  }
  @Override
  public String getAsString( FacesContext context, UIComponent component, Object value) {
    String result = "";
    int i = 0, n = 0;
    if ( value == null) {
      return null;
    } else if ( value instanceof Set<?>) {
      n = ((Set<Book>) value).size();
      for ( Book b : (Set<Book>) value) {
        result += b.getTitle();
        if ( i < n - 1) {
          result += "<br />";
        }
        i++;
      }
      return result;
    }
    return null;
  }
}

Then, we use the converter attribute in the JSF view to specify that our converter class, e.g., pl.model.converter.BookListConverter, has to be used when the respective view component needs to be rendered:

<h:column>
  <f:facet name="header">Published books</f:facet>
  <h:outputText value="#{p.publishedBooks}" escape="false" converter="pl.model.converter.BookListConverter"/>
</h:column>

Since the serialization text contains HTML elements, i.e., <br /> to produce HTML new lines, we have to specify the @escape = "false" attribute, otherwise < and > will be replaced with the corresponding &lt; and &gt; entities.

The main advantage of using JSF converters is that we do not mix model code with view specific code, so our model classes remains clean and later, if we like to replace JSF with other framework, then our model class reamins unchanged, and we just need to take care of a corresponding converter for the new framework.