• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Bear Bibeault
  • Knute Snortum
  • Liutauras Vilda
Sheriffs:
  • Tim Cooke
  • Devaka Cooray
  • Paul Clapham
Saloon Keepers:
  • Tim Moores
  • Frits Walraven
  • Ron McLeod
  • Ganesh Patekar
  • salvin francis
Bartenders:
  • Tim Holloway
  • Carey Brown
  • Stephan van Hulst

Displaying the contents of a Hashmap in a dataTable  RSS feed

 
Ranch Hand
Posts: 177
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I created the following method which returns a HashMap<Date, List<Date>> where the Key is a Date object and the value is a List of Date Objects. The objective was to take a List of timestamps and group them by day. This method returns those grouped timestamps:





This is my dataTable. As you can see, currently I am outputting the userLogins. Obviously, I'm seeing the hashcode values of the object. What I actually want to display are the keys. Then I will create a row expander to show the list of time stamps for each key. How do I navigate my way through my Hashmap data structure?

 
Bartender
Posts: 20125
103
Android Eclipse IDE Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Well, first, I don't recommend actually returning the raw data as a value to a dataTable.

In order to manage the rendering process, a dataTable needs a corresponding DataModel object as a wrapper (façade). In the case where you simply return a raw array or collection of data, JSF must construct an anonymous DataModel automatically. Nothing inherently wrong with that, except that the DataModel happens to have some properties that are very useful.

In particular, the getRowData method, which can return the data of a selected row (for example, the row in which you clicked a button or link), and the getRowIndex method, which returns the 0-based index of the selected row.

If you construct your own DataModel object, wrap it around your raw data and return that as the value property, you will have done the same thing that JSF did anyway, but now you have an object that your action methods and AJAX listeners can use to understand context without having to fumble through a bunch of unnatural and awkward circumventions. DataModels are easy to use. Just construct them and make them available via property-set. You don't have to re-wrap the data if the data changes unless you completely replace the data collection object with a new object. Changes to data within a collection don't require re-wrapping it.

Which brings me to the second point. The collection that the dataTable represents must be a sequential collection. That is, a List, array, or other collection that has a natural order. Yes, you can enumerate any collection, but the order in which the elements of that collection are returned must be precisely defined. In the case of a hash, changing even one element can completely scramble the order of the returned items. It's also worth nothing that the collection must return the exact same sequence multiple times (be idempotent), since JSF may call the value "get" method up to about 6 times for a single page request. And thus, also, you don't want to do any expensive operations such as database access in the data set while its value get method is executing.

And now to the specifics. A HashMap internally is a collection of key/value pairs. So when you enumerate the map, what the enumerator is actually returning on each element is an internal object containing one key, one value, and possibly some overhead (such as a hash collision link). That object doesn't implement a useful toString() method since it's not intended to be used directly. Besides, a dataTable usually has multiple columns, so you need to indicate what data goes in what column.

Oh. And one more thing. You're returning a collection containing multiple sub-collections. A dataTable only maps the primary collection. You'll need to flatten out the model to deal with that.

So here's some possibilities.

First, consider an ordered collection as the basis for your dataTable Model. Secondly, make the model elements be something that JSF can deal with as objects with properties or can convert to an array of Strings. In the case of a Map, what I'd normally do is construct a List or Array containing Map elements as user-defined objects, but here's one alternative:



Hopefully you can translate your getUserLogins method to build the login Record List. That's more work than I can justify for this example.

Note that this code has the very useful property that if your model becomes outdated, you can simply set loginRecordModel to null and a fresh model will be built the next time it's needed. That also handles the initial construction more gracefully than trying to do so in the backing bean constructor (which often doesn't have enough context) or a @PostConstruct method.

And now, finally, the dataTable:


Actually, there's something odd about your datatable elements, but since I don't work regularly with PrimeFaces, I'm not sure exactly how to lay it out.

And finally, since this is the sort of thing where you'd like to be able to expand/collapse and/or highlight rows by the major key (loginId), PrimeFaces does have an easy way to do that, but that, too is beyond the scope of this example.

Incidentally, I cheat. I've defined a set of boilerplate snippets in my IDE to handle the grunt work of setting up the dataModel and building its payload because I've done it so often.
 
Mark Richardson
Ranch Hand
Posts: 177
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thank you, Tim!

In our implementation, by the time we get the userLogins they are already part of a User object which belongs to a specific user. Therefore, there is no need to provide "user id" - and unfortunately, I have to work within that paradigm on this project, and furthermore, we've been pigeon-holed by our lead to use the Hashtable construct.

What we get back will be ALL the logins for that one user.

Here is what the data looks like in our Hashtable construct:

Key                          List<Date>
2018-07-11
                 2018-07-11 08:14:08.540000
                 2018-07-11 10:46:23.575000

2018-07-12  
                 2018-07-12 12:51:48.928000
                 2018-07-12 13:09:00.701000
                 2018-07-12 16:04:45.890000

2018-07-13
                 2018-07-13 14:14:17.461000


Now, the question is... how to use the square brackets in JSF to go and fetch the keys (or the actual time-stamps)
On the UI the keys will be listed and next to them will be row expanders which will reveal the time-stamps.
 
Mark Richardson
Ranch Hand
Posts: 177
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Mark Richardson wrote:HashTable

Typo: I meant to say, HashMap.
 
Tim Holloway
Bartender
Posts: 20125
103
Android Eclipse IDE Linux
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Great Chthulhu preserve us from "genius" managers and architects who place unnatural constraints on projects based on misunderstood concepts.      

A table is a 2-dimensional construct. It has rows and columns. The table data model is precisely what its name says - a Model of the data that the table will display. Therefore the model must also resolve to rows and columns.

Maps are not tables. They don't have rows and columns, they have keys and values. And, in your case, the values aren't scalars, they are vectors. That is about as non-flat a setup as anyone could find without severe straining. In fact, the graphical equivalent of your data is more like a table where one of the cells is a sub-table.

It is possible to nest tables in JSF, but not easily. It requires considerable fudging, because unfortunately JSF wasn't designed to support it, and while I hope that some day it will, that day isn't expected any time soon.

On the other hand, PrimeFaces is one of several JSF extensions that does permit collapsing a table based on a common element value, so you can get something like what you're envisioning by using PrimeFaces. But Primefaces is expecting - or rather demanding a flat table datamodel. If your primary datamodel does not conform, then it is your responsibility to construct a GUI datamodel that conforms to what Primefaces requires.

It is, incidentally, a common fallacy that when you have an ORM datamodel that that datamodel should always directly serve as the JSF GUI datamodel. They are 2 different types of models, serving 2 different purposes. It's convenient when you can make one set of objects serve for both purposes, but not always possible. A very common situation where that occurs is when you have a DBMS that doesn't have a native boolean data type but you want checkboxes on a JSF form. Because the get/set methods for checkboxes have to be boolean methods or JSF will refuse to work with them. Thus it is sometimes required to construct a façade object to serve as the GUI datamodel.

 
Mark Richardson
Ranch Hand
Posts: 177
2
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator

Tim Holloway wrote: Primefaces

Yep! That's exactly what we're using. But, I'm straining to figure out how to get all the key values out of my collection and display them in my dataTable. As for "genius" managers, since I'm a Jr. myself, I don't exactly have the experience, foresight, nor clout to recommend alternate solutions
 
Tim Holloway
Bartender
Posts: 20125
103
Android Eclipse IDE Linux
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
A "genius" manager is, as I implied, one who sets rules arbitrarily. Because in his infinite wisdom, he is Right. This is almost the opposite of what is considered to be the ideal manager, who may or may not know anything technical at all, but knows enough to avoid dis-incentiving his team. Good IT people don't have to be driven, since they'll drive themselves harder than anyone with a whip could drive them, so long as they're not discouraged.

But the "genius" hears some snippet of knowledge and makes an absolute and inviolable Law out of it. Thou Shalt Not this, Thou Shalt Not That, Thou Absolutely Must do This. Often they have detailed reasons for these rules, which, alas, often are based on incomplete understanding and/or outdated information. And often, it isn't actually the manager who's doing this, but some senior architect type who the manager looks to to determine what the Law Must Be.

It's not that we're into complete anarchy here, but we are into Best Practices. Best Practices are simply strategies that have stood the test of time, and they're not Inviolable Law, because sometimes the practice that actually works best for a specific case isn't what works best for a particular situation at a particular shop. The key is to keep to standards where possible while not forbidding exceptions when the standards would do more harm than good. For example, by making program logic excessively complicated and/or inefficient, or by hampering the productivity of  the developers and maintainers.

To make our position at the Ranch plain, we used to have a bot that would automatically flag the word "should" in messages and link to our "Did you just 'should' me?" FAQ entry. I think it finally got turned off because not every use of the word "should" in English is actually a command, but the idea was to make sure that we didn't get too authoritarian in our advice. Because in the end, that's what we're here for: advice. Not ready-made solutions, not cookbook recipes, but advice on how best to do what needs to be done, based on our own knowledge and experience.

It's no crime to be inexperienced. We're here to help with that. But we do our best work when everyone's pulling together. When we say "this works" and the boss says "you can't do that", and the work-arounds are awful, we'll say so. And if the work-arounds are either impossible within the constraints of the framework or prohibitively expensive, we'll say so as well. And in cases where someone does come up with a graceful solution, we will, I hope, be gracious enough to add that to our knowledge base and be thankful.

If someone comes to me with a problem with their login system and it's an in-house designed system that either they or the resident security "genius" invented, I'm going to give the standard lecture about how about 95% of all user-designed web security systems can be cracked in 15 minutes or less by non-technical personnel (based on my experience with a lot of financial and even some military systems, as well as similar experiences from other ranchers) and that it's much wiser to use the system invented by full-time trained security professionals and integral to JEE, but having done so, I and my partners in crime here on the Ranch will do our best to resolve the issue presented to us.

As I said previously, your main problem is that you're trying to fit a data structure that's essentially 3-dimensional into a display framework intended for 2 dimensions and the only clean and easy way to do that is to remap it into a 2-dimensional model. In Java this is not all that expensive, since 2 different maps of the same set of objects only carry the overhead of the relationships, and not the objects themselves. And, as you probably noted, I've a mechanism where the mapping only needs to be done when the data and relationships change, since once the model object is created, I cache it for re-use.
 
When you have exhausted all possibilities, remember this: you haven't - Edison. Tiny ad:
RavenDB is an Open Source NoSQL Database that’s fully transactional (ACID) across your database
https://coderanch.com/t/704633/RavenDB-Open-Source-NoSQL-Database
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!