This week's book giveaway is in the Performance forum.
We're giving away four copies of The Java Performance Companion and have Charlie Hunt, Monica Beckwith, Poonam Parhar, & Bengt Rutisson on-line!
See this thread for details.
Win a copy of The Java Performance Companion this week in the Performance forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Design advice on how to implement preferences and language support

 
Stephane Eybert
Ranch Hand
Posts: 34
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello,

I have a legacy php application which has multiple modules, each one having a set of preferences supporting language localized strings.

For example, the "document" module has the following preferences:



The language text strings are stored in text files sitting in the file system next to the script like:

DocumentUtils.php
.DocumentUtils.en.php
.DocumentUtils.se.php

Here is the content of one such language text file:

[ 0]Document to download:
[ 1]Download the document
[ 2]Ref:
[ 3]Download
[ 4]Display all the documents:
[ 5]By default, when no category has yet been selected by a visitor of the website, only the documents of the first category are displayed.\n\nBut it is possible to display all the documents instead of only the ones of the first category.
[ 32]Number of documents per row:
[ 33]The lists of documents can display several documents per row.\n\nThe number of documents per row can be modified.\n\nThe number of images per row can be modified.
[ 6]Hide the selector:
[ 7]By default, a category selector is displayed on top of the list of documents.\n\nBut this selector can be hidden to prevent the selection of another category.

The preferences values are stored in a database table:

mysql> desc preference;
+---------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+---------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| version | int(10) unsigned | NO | | NULL | |
| name | varchar(50) | NO | UNI | NULL | |
| value | text | YES | | NULL | |
| type | int(10) unsigned | NO | | NULL | |
+---------+------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)

mysql> select * from preference limit 10;
+----+---------+----------------------------+-------------+------+
| id | version | name | value | type |
+----+---------+----------------------------+-------------+------+
| 1 | 0 | FLASH_INTRO_HIDDEN | 1 | 1 |
| 2 | 0 | FLASH_INTRO_DISPLAY_ONCE | 1 | 1 |
| 3 | 0 | FLASH_INTRO_DISPLAY_PERIOD | 0 | 7 |
| 4 | 0 | FLASH_INTRO_POPUP | 1 | 1 |
| 5 | 0 | FLASH_INTRO_PAGE_BG_COLOR | | 8 |
| 6 | 0 | FLASH_INTRO_SKIP_LINK | | 2 |
| 7 | 0 | FLASH_INTRO_POPUP_WIDTH | 900 | 2 |
| 8 | 0 | FLASH_INTRO_POPUP_HEIGHT | 900 | 2 |
| 9 | 0 | CLOCK_DATE_NUMERIC_FORMAT | %d-%m-%Y | 6 |
| 10 | 0 | CLOCK_DATE_FORMAT | %A %e %B %Y | 6 |
+----+---------+----------------------------+-------------+------+
10 rows in set (0.00 sec)

And so now I wonder how to go to code a preference manager that would be compatible with the existing data.

I already have the data access layer in Java including domain class, mutators and finder..

But I still need the coupling with the hard coded names in the code and the languages text strings.

Any tip would be welcome.

Thanks !

Stephane

 
Rob Spoor
Sheriff
Pie
Posts: 20546
57
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think you should check out ResourceBundle. It's one of the most useful classes when dealing with internationalization (no wonder they shorten that to I18N!).
 
Stephane Eybert
Ranch Hand
Posts: 34
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Thanks Rob. I used the ResourceBundle class in the past.

My issue lies more with the constants and how to load them:



I guess I could have in each module, an enum along the lines of:

public enum Preference {

DOCUMENT_NB_PER_ROW("My label1", "My help1", PREFERENCE_TYPE_RANGE, array(1, 10, 3)),
DOCUMENT_DISPLAY_ALL("My label2", "My help2", PREFERENCE_TYPE_BOOLEAN),
DOCUMENT_HIDE_SELECTOR("My label3", "My help3", PREFERENCE_TYPE_BOOLEAN);

private String label;
private String help;
private int type;
private Array possibleValues;

Preference(String label, String help, int type) {
this.label = label;
this.help = help;
this.type = type;
}

Preference(String label, String help, int type, Array possibleValues) {
this.label = label;
this.help = help;
this.type = type;
this.possibleValues = possibleValues;
}

public String getLabel() {
return this.label;
}

public String getHelp() {
return this.help;
}

public String getType() {
return this.type;
}

}

As I would have such an array in each module of the application, would it be possible to have each enum class extend on a common one ?

Something like an enum extending anoterh base enum:

http://freddy33.blogspot.com/2007/05/why-java-enum-cannot-extends.html
 
Rob Spoor
Sheriff
Pie
Posts: 20546
57
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Unfortunately not. You can never have an enum explicitly extend a class; they all extend java.lang.Enum automatically.
 
Stephane Eybert
Ranch Hand
Posts: 34
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I could skip the enum then.

Any design advice ?
 
Rob Spoor
Sheriff
Pie
Posts: 20546
57
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
You can mimic an enum for all features except the possibility to use it in switch statements.
First, you start by making all constructors private.
Second, you just create a couple of public static final fields of the same type that represent your enum constants.
That already gives you the bulk of the functionality. The rest can be added as needed.
 
Stephane Eybert
Ranch Hand
Posts: 34
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ok Rob, I shall try that road then, cheers.
 
Campbell Ritchie
Sheriff
Pie
Posts: 49400
62
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think this question is too difficult for “beginning”, so I shall move it.
 
Stephane Eybert
Ranch Hand
Posts: 34
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I tried to come up with a design but I'm mildly satisfied with my production here.

So here is what I did.

There is a common Preference class:



Which is used by other Preference classes, each for a module of the application:



The application module can then read the preference value with:



I don't know why, but I don't like it.
 
Stephane Eybert
Ranch Hand
Posts: 34
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I think the preferences of one module should sit in an array so as to be traversable and displayed in a list.
 
Rob Spoor
Sheriff
Pie
Posts: 20546
57
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephane Eybert wrote:

1) All of those constants should be static, not just the first one.
2) I would instead use a nested enum:
After that you replace all of those int types to Type. For instance:

I don't know why, but I don't like it.

Why not? Your common Preference class actually looks quite a lot like java.lang.Enum, and your "concrete" Preference class looks like an enum class.

All that you're now missing (apart from stuff like serialization) is the static valueOf() and values() methods. You can get those to work with a few static utility methods. Inside your common class you add global support, using reflection:
Each Preference subclass now gets a little code to support this:

For the pseudo code you should look at the methods of java.lang.reflect.Field, java.lang.reflect.Modifier and java.lang.Class. The exceptions thrown by field.get(null) can be caught and printed; they won't occur since the field is checked to be public, static and final. That means that a) an IllegalAccessException won't occur, b) an IllegalArgumentException won't occur, and c) a NullPointerException won't occur.
 
Stephane Eybert
Ranch Hand
Posts: 34
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I tried a new take, this time not using the enum construct.






 
Stephane Eybert
Ranch Hand
Posts: 34
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello Rob,

I just see your last post, sorry for not having seen before, strange because I made sure to refresh the page before my last post.

Stephane

 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic