tags:

views:

46

answers:

2

Preface: I am a long-time fan of SO, read stuff here almost everyday -- but this is my first question -- thank you to everyone who has made SO such a fantastic resource!

Environment: Spring/JSTL/Java ; the constraints of this project are such that I cannot accept a solution which requires abandoning Spring/Java, though I could probably implement any standard view in place of JSTL.

Note: This question is not about how to iterate over a simple collection in JSTL; I know how to do that. Additionally, I have searched SO and Google for an answer to this problem and have yet to find an answer. I am willing to admit that maybe that indicates that I am approaching the problem incorrectly -- if so, I would appreciate an explanation of a better way to approach it.


Problem: I have a data-service from which I obtain a collection of collections. I do not control this service, nor am I able to define it -- someone else is in charge of that, and it can change at any time. Each item in the collection can be another collection (which I must parse), so the entire collection is theoretically unbounded. The collection is basically like this:

  • Item name : String
    • Item : Object (can be a String or another Collection)
      (this sequence is potentially infinite)

Essentially each item is a "service" that my client offers to their customers -- for example: "Configure your institutional-email", and each collection is a "category" of related services -- for example: "Email Services Offered". But the category of "Email Services Offered" could actually be an item/object/collection of a larger item/object/collection that is more general -- for example: "Communication Services Offered".

Current Solution: In my Spring Controller I currently get this collection as a Map and place it onto my Model. In my View (JSTL) I (currently) iterate through 2-levels of the collection, where I assume (the one thing I can assume about this) that the first level is all going to be sub-collections (ie: 'categories'), and where I also assume (incorrectly) that the second level is all going to be items (ie: 'services').

<ul>    
  <c:forEach items="${serviceCategories} var="category">  // iterate through each collection

    <li>${category.key}  //the user-displayable title of the 'category' ('collection')

      <ul>
        <c:forEach items="${category.value}" var="service">  // iterate through each item
          <li>${service.key}</li>  //the user-displayable title of the 'service' ('item')
        </c:if>
      </ul>

    </li>

  </c:if>
</ul>

So I guess my question is that, given such a data-structure, how do I represent it in a view that can manage this??? I am bound by the client's desire to have this structure represented in a tree-like structure.

If I could manipulate the view using pure-java then I would expect one way to achieve this would be to use recursion... something like this, I guess:

/** NOTE:  I am not suggesting this is a great example of recursion, nor that it even
 *  would result in correct output; it was one-off'd just to provide something of an 
 *  example of how I imagine this could be done in Java if I had to do it this way.
 */
private StringBuilder buildString( Map<String, Object> dataStruct ) {

  StringBuilder view = new StringBuilder();

  Set<String> keys = dataStruct.getKeys();
  for( String key : keys ) {

    Object value = dataStruct.get( key );
    if( value instanceof String ) {

      view = key + "[" + value + "]";
      return;
    }

    String subView = buildString( value );
    view = key + "[" + subView + "]";
}

Gosh, I hope that is all clear-as-mud and not overwhelming. I also hope I am simply overlooking (or ignorant of) a more elegant-solution that is readily recognized by someone who can help me out.

Thank you for your time! :D

A: 

Anytime you have hierarchical data that has unknown depth, and you want to display it, I think a good option is to turn it into XML and bind the XML to some type of tree display. For example, in a similar situation in one of my applications (also Java/Spring) I use XStream to turn the collection into XML (you simply point XStream at the root object and it follows all of the paths for you), and then I display the results using the jQuery plugin jsTree.

If you don't want to use javascript, an alternate approach would be to use something like Jakob Jenkov's Tree Tag.

JacobM
Thanks for the suggestions -- and so fast! :D As I commented in my original post, I'm going to go the TreeTag-approach due to the fact that I had already decided a custom JSP-tag was the way to go but this has the luxury of being already written and is more likely to be more browser-compliant than what I'd write -- plus, it's still totally customizable. Thank you very much for the excellent advice! :)
Bane
Crap, I'd love to vote for your answer -- but being an SO-newb, it won't let me vote yet. :(
Bane
Thanks -- even if you can't vote, you should be able to click the checkbox to the left to "accept" the answer.
JacobM
You're right, done. Thank you again. :)
Bane
SOLUTION-UPDATE: I tried to implement TreeTag and found I could not get it to work... it was throwing a NullPointerException when it's JSP-tags attempted to parse the tree-object I'd placed on the model -- Jenkov tried to help but in the end we couldn't get it to work. So I ended up going with this: 3 Java classes (Node, Leaf, Element) which I parsed my Map into; then I used Flexjson to serialize the object and put it on my model; then I implemented javascript to parse the json into an unordered html-list; finally I applied the jQuery plugin jsTree to render. Works beautifully! :D
Bane
Nice! And so simple. :) But actually sounds like a pretty good architecture.
JacobM