0

I am using the following data structure to store JDBC results in a Servlet controller prior to displaying in a JSP view using JSTL.

TreeMap
  - TreeMap
      - String[]

Four columns of data are returned per row.

"CATEGORY","OSDIRECTORY","FILENAME","DESCRIPTION"
"CATEGORY","OSDIRECTORY","FILENAME","DESCRIPTION"
"CATEGORY","OSDIRECTORY","FILENAME","DESCRIPTION"
etc.

The goal is to store the results in the data structure as

Category
    - FILENAME
        - OSDIRECTORY
        - DESCRIPTION

And to display the final results in the View as

Category A
    Hyperlink
    Hyperlink
    Hyperlink
Category B
    Hyperlink
    Hyperlink
etc.

Relevant Servlet Controller Code Snippet

...
TreeMap treeMap = new TreeMap();

rs = stmt.executeQuery(query); 

// Gather raw data
while(rs.next()){

    if(!treeMap.containsKey(rs.getString("CATEGORY"))){
        treeMap.put(rs.getString("CATEGORY"), new TreeMap());
    }

    String[] tmp = { rs.getString("OSDIRECTORY"), rs.getString("DESCRIPTION") };
    ((TreeMap)treeMap.get(rs.getString("CATEGORY"))).put(rs.getString("FILENAME"), tmp);
}

request.setAttribute("filemap", treeMap);

RequestDispatcher rd = request.getRequestDispatcher(VIEW_URL);

rd.forward(request, response);
...

Relevant JSP View JSTL Snippet

<c:forEach var="f" items="${filemap}">
    <h1><c:out value="${f.key}"/></h1>
    <c:forEach var="g" items="${filemap[f.key]}">
      <a href="TBD">
        <c:out value="${filemap[f.key][g.key][0]}"/>
        <c:out value="${filemap[f.key][g.key][1]}"/>
      </a>
    </c:forEach>
</c:forEach>

I am wondering of there is a more concise way to express some of the JSTL expressions.

For example ${filemap[f.key][g.key][0]} just seems too verbose to me.

T.P.
  • 2,975
  • 1
  • 21
  • 20

2 Answers2

1

I would make objects to represent your data instead of using a map of maps. Since you have categories that contain files, make a category and file objects that look something like this:

public class Category {
    private String name;
    private List<DbFile> files = new ArrayList<DbFile>();
    // getters & setters for each property go here
}

public class DbFile {
    private String filename;
    private String osDirectory;
    private String description;
    // getters & setters for each property go here
}

Then in your data access code you can iterate over the result set and build up a list of categories objects like this:

Map<String, Category> categoryMap = new TreeMap<String, Category>();

while(rs.next()){
    String categoryName = rs.getString("CATEGORY");
    Category category = categoryMap.get(categoryName);

    if(!categoryMap.containsKey(categoryName)){
        category = new Category();
        category.setName(categoryName);
    }

    DbFile file = new DbFile();
    file.setFilename(rs.getString("FILENAME"));
    file.setOsDirectory(rs.getString("OSDIRECTORY"));
    file.setDescription(rs.getString("FILENAME"));

    category.getFiles().add(file);
    categoryMap.put(categoryName, category);
}

request.setAttribute("categories", Arrays.asList(categoryMap.values()));

Then the code on your page is much more straightforward:

<c:forEach var="category" items="${categories}">
   <h1><c:out value="${category.name}"/></h1>
   <c:forEach var="file" items="${category.files}">
       <a href="TBD">
           <c:out value="${file.osDirectory}"/>
           <c:out value="${file.description}"/>
       </a>
    </c:forEach>
</c:forEach>
clav
  • 4,221
  • 30
  • 43
  • Unfortunately, I'm stuck with JDK1.4 so Generics are not possible. String arrays were used for most of the legacy code and unfortunately there is not enough time to sweep and do a large scale refactor. – T.P. May 01 '13 at 17:12
  • JDK 1.4 is fine, just remove the generics. In other words `Map categoryMap = new TreeMap();` becomes `Map categoryMap = new TreeMap();`. If by "large scale refactor" you mean you can't replace the map of maps containing String arrays with with objects, then you're limited to how clean you can make it, at some point you have to access `map[key][0]`. – clav May 01 '13 at 22:58
0

After some later investigation and experimentation, I found this to be a cleaner approach than the JSTL posted in the question.

<c:forEach var="f" items="${filemap}">
    <h1><c:out value="${f.key}"/></h1>
    <c:forEach var="g" items="${f.value}">
        <a href="TBD">
            <c:out value="${g.value[0]}"/>
            <c:out value="${g.value[1]}"/>
        </a>
    </c:forEach>
</c:forEach>
T.P.
  • 2,975
  • 1
  • 21
  • 20