I need to code up an app in a super hurry, so I'm unfortunately forced to leverage existing toolkits, and one module will be a survey, so this toolkit looks like one of the most promising. But, after reading the docs and experimenting a bit, I've found what seem to be a few disappointing lack of features -- I'm hoping someone can point out to me that I've made a mistake or there's some workaround.
For one thing, I'd like to set the value of an answer dynamically during the survey (i.e. presumably from within the script). E.g. I have a height in inches and in centimeters; when the user sets the value in the inches height field, I'd like to pre-load the textField of the height_cm question's answer to the same value in converted units. I tried just setting the height_cm variable but it didn't seem to work. Am I missing something? I saw answerOf() but no setAnswerOf() in the docs. It might be nice to even be able to modify some labels' values from script, but the answers are the most important to me.
I also would like to be able to pre-load default values in some of the answers from the calling Java app. In fact, I'd like to set up all sorts of things from the calling app -- set some questions visible, enabled, populate templates' radio-button values, etc. While some of this could be done via script, it's not convenient because I need to "pass in" many parameters from the calling app. While I could modify (or create from whole cloth) the XML file from the calling app, generating beanshell code, etc., then let JSurveyLib parse it, this seems outrageously Rube Goldberg, when the app and the survey objects are sitting in the same VM, perfectly able to pass programmatic messages to each other -- and even then I noticed nothing in the XML spec to set a default-value for an answer.
Perusing the javadocs a little, I see that there are classes and objects for things like questions that presumably could be pragmatically manipulated, but most are, at best, marked "FOR INTERNAL USE ONLY" (one also wonders why some of the internal-only methods do not use 'protected' access-control) -- and furthermore the general design doesn't seem to be very friendly towards interaction between the calling app and the survey other than via XML: e.g. the 'Question' class has facilities for things like a validationListener, but no 'answerChangedListener' (that I can see) other than the ability to set the onAnswerChanged _script_; should I want to perform any validation via actual java code rather than script, this would force me to dig deeper into the guts of the library to add listeners to things like the text-fields themselves. Forgive my ignorance of beanshell, but is there any way to call methods in my host Java code from the script?
Furthermore, while the combination of the built-in question-types with beanshell is incredibly powerful, it would be nice to be able to embed a Java component of my own as an answer-type, for example a date-field that can pop up a calendar-style day-picker; there seems to be no provisions made for this, short of heavily hacking into the library itself.
Nonetheless, if I can access the "INTERNAL ONLY" classes and methods, combined with some script, I might be able to cobble up something usable, so... while not recommended, does that seem a workable solution, or is there a pitfall that will stop me? Is there some cleaner way (or just other unclean way) that I've overlooked to accomplish these things? I've only got precious little time to do the whole app, so I'd just as soon not have to code up my own survey module as well as the rest, at least for the initial release.
Any advice is greatly appreciated.
Thanks,
David
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Thanks for the reply. You bring up a lot of good points, I hope I address every one:
"I'd like to set the value of an answer dynamically during the survey" That is not currently possible. I've been putting it off for as long as possible. The reason is because the questions act so differently from each other. Also, the radio buttons are a little unique in that they can default to a value of "" but you can't legally set them to "" if they've already been set to something else. I'm sure there's a lot of edge cases like this. Rather than delve into the details of this, I decided to procrastinate until someone like you came along so I could iron out the details :)
In the mean time, you could add this function yourself. src\org\jsurveylib\model\script\interpreter\commands\ you'd create a setAnswerOf.bsh and populate it with this:
void answerOf(String id, String answer) {
if (ENVIRONMENT.get("questions").get(id) == null) return null;
ENVIRONMENT.get("questions").get(id).setAnswer(answer);
}
Then in src\org\jsurveylib\model\script\interpreter\commands\LoadAllCommands.java you'd add "setAnswerOf" to the array named commandNames. I think tha'd do it. But it may have some side effects you wouldn't expect. For example, calling that script will probably notify all onAnswerChanged listeners which will in turn evaluate the scripts again. But you're already evaluating a script when you call that method so... weird things may happen.
"It might be nice to even be able to modify some labels' values from script" Yeah, that could be very useful. I want to make sure I do that in a clean way (from the script writer's perspective). There could be a setLabelOfAnswer(questionId, newLabel); script. Unfortunately, the labels outside of the question don't require an id, so it's not so straightforward for them. Perhaps I should allow every label to have an id and then I could just make a single setLabel(labelId, newLabel); script.
"I also would like to be able to pre-load default values in some of the answers from the calling Java app. In fact, I'd like to set up all sorts of things from the calling app -- set some questions visible, enabled, populate templates' radio-button values, etc." I've also been thinking about this myself. Right now there is only one way to build a survey. It expects an XML configuration that implements SurveyReader. To provide all this functionality you're talking about I was thinking about making a new type of SurveyReader that you build in java. I haven't thought this through completely but maybe you'd interact with the interface like this:
JavaSurveyBuilder builder = new JavaSurveyBuilder(); //implements SurveyReader
builder.addPage().addQuestion("textId", "Text Label", TEXT_FIELD).addQuestion("rbId", "Radio Buttons Label", RADIO_BUTTONS, rbChoices).addQuestion(...);
builder.addPage()...
builder.addInitScript("my script")...
...
ClientSurvey survey = new Survey(builder);
...
"the general design doesn't seem to be very friendly towards interaction between the calling app and the survey other than via XML" Yes that is by design. I know that I will make api mistakes that I wish I could change later. To make this easier on me, I default to making any class/method for internal use only so that I may change it later without having to worry about breaking someone else's code. Do you think the JavaSurveyBuilder I described above will be sufficient for your needs?
"the 'Question' class has facilities for things like a validationListener, but no 'answerChangedListener' (that I can see) " I'm a little confused. The Question class has a list of AnswerListener. Is that what you were looking for? If not, I don't understand the question.
"Forgive my ignorance of beanshell, but is there any way to call methods in my host Java code from the script?" That's an interesting request I never considered before. In a sense, I'm already doing that. If you look at src\org\jsurveylib\model\script\interpreter\ScriptInterpreter.java#setupEnvironment, you'll see that's what this method is doing. The interpreter.set method adds the survey instance to the command's scope so they can call the survey later.
"Furthermore, while the combination of the built-in question-types with beanshell is incredibly powerful, it would be nice to be able to embed a Java component of my own as an answer-type, for example a date-field that can pop up a calendar-style day-picker; there seems to be no provisions made for this, short of heavily hacking into the library itself." Yes, there are no provisions made for this. I considered this at one point but decided to not make it possible. I can't remember all of my reasoning, but I know one important reason is it would prevent me from changing the api later. If I could add some extremely powerful functionality by adding a parameter to a method, I didn't want to be held back by the fact that it may break some other library's code. Also, you really do need to know a lot about the library to add a new question type. I believe it touches every layer of the app. You'd need to add your own renderer and figure out a way to update XMLSurveyReader too. So, in summary, it wouldn't be trivial. That, and the fact that jsurveylib is new enough that it could really benefit by a date picker like the one you described being added to it directly as opposed to an outside add-on.
"Nonetheless, if I can access the "INTERNAL ONLY" classes and methods, combined with some script, I might be able to cobble up something usable, so... while not recommended, does that seem a workable solution, or is there a pitfall that will stop me?" The pitfall to that is that the next version of jsurveylib may change the api in a way so that your code won't compile. If you don't plan on upgrading to the next jsurveylib, it shouldn't effect you. If you don't mind changing your code after you upgrade, it shouldn't effect you.
"I've only got precious little time to do the whole app, so I'd just as soon not have to code up my own survey module as well as the rest, at least for the initial release." I understand your issues. As I've mentioned, most of these feature requests are already planned for in the future. Unfortunately, none of them are required for what I use jsurveylib for at my day job so I can not say when they will be added. BTW, what other survey/questionnaire libraries have you looked at? I hope this has helped, let me know if you have any other questions.
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Hi,
I need to code up an app in a super hurry, so I'm unfortunately forced to leverage existing toolkits, and one module will be a survey, so this toolkit looks like one of the most promising. But, after reading the docs and experimenting a bit, I've found what seem to be a few disappointing lack of features -- I'm hoping someone can point out to me that I've made a mistake or there's some workaround.
For one thing, I'd like to set the value of an answer dynamically during the survey (i.e. presumably from within the script). E.g. I have a height in inches and in centimeters; when the user sets the value in the inches height field, I'd like to pre-load the textField of the height_cm question's answer to the same value in converted units. I tried just setting the height_cm variable but it didn't seem to work. Am I missing something? I saw answerOf() but no setAnswerOf() in the docs. It might be nice to even be able to modify some labels' values from script, but the answers are the most important to me.
I also would like to be able to pre-load default values in some of the answers from the calling Java app. In fact, I'd like to set up all sorts of things from the calling app -- set some questions visible, enabled, populate templates' radio-button values, etc. While some of this could be done via script, it's not convenient because I need to "pass in" many parameters from the calling app. While I could modify (or create from whole cloth) the XML file from the calling app, generating beanshell code, etc., then let JSurveyLib parse it, this seems outrageously Rube Goldberg, when the app and the survey objects are sitting in the same VM, perfectly able to pass programmatic messages to each other -- and even then I noticed nothing in the XML spec to set a default-value for an answer.
Perusing the javadocs a little, I see that there are classes and objects for things like questions that presumably could be pragmatically manipulated, but most are, at best, marked "FOR INTERNAL USE ONLY" (one also wonders why some of the internal-only methods do not use 'protected' access-control) -- and furthermore the general design doesn't seem to be very friendly towards interaction between the calling app and the survey other than via XML: e.g. the 'Question' class has facilities for things like a validationListener, but no 'answerChangedListener' (that I can see) other than the ability to set the onAnswerChanged _script_; should I want to perform any validation via actual java code rather than script, this would force me to dig deeper into the guts of the library to add listeners to things like the text-fields themselves. Forgive my ignorance of beanshell, but is there any way to call methods in my host Java code from the script?
Furthermore, while the combination of the built-in question-types with beanshell is incredibly powerful, it would be nice to be able to embed a Java component of my own as an answer-type, for example a date-field that can pop up a calendar-style day-picker; there seems to be no provisions made for this, short of heavily hacking into the library itself.
Nonetheless, if I can access the "INTERNAL ONLY" classes and methods, combined with some script, I might be able to cobble up something usable, so... while not recommended, does that seem a workable solution, or is there a pitfall that will stop me? Is there some cleaner way (or just other unclean way) that I've overlooked to accomplish these things? I've only got precious little time to do the whole app, so I'd just as soon not have to code up my own survey module as well as the rest, at least for the initial release.
Any advice is greatly appreciated.
Thanks,
David
Thanks for the reply. You bring up a lot of good points, I hope I address every one:
"I'd like to set the value of an answer dynamically during the survey" That is not currently possible. I've been putting it off for as long as possible. The reason is because the questions act so differently from each other. Also, the radio buttons are a little unique in that they can default to a value of "" but you can't legally set them to "" if they've already been set to something else. I'm sure there's a lot of edge cases like this. Rather than delve into the details of this, I decided to procrastinate until someone like you came along so I could iron out the details :)
In the mean time, you could add this function yourself. src\org\jsurveylib\model\script\interpreter\commands\ you'd create a setAnswerOf.bsh and populate it with this:
void answerOf(String id, String answer) {
if (ENVIRONMENT.get("questions").get(id) == null) return null;
ENVIRONMENT.get("questions").get(id).setAnswer(answer);
}
Then in src\org\jsurveylib\model\script\interpreter\commands\LoadAllCommands.java you'd add "setAnswerOf" to the array named commandNames. I think tha'd do it. But it may have some side effects you wouldn't expect. For example, calling that script will probably notify all onAnswerChanged listeners which will in turn evaluate the scripts again. But you're already evaluating a script when you call that method so... weird things may happen.
"It might be nice to even be able to modify some labels' values from script" Yeah, that could be very useful. I want to make sure I do that in a clean way (from the script writer's perspective). There could be a setLabelOfAnswer(questionId, newLabel); script. Unfortunately, the labels outside of the question don't require an id, so it's not so straightforward for them. Perhaps I should allow every label to have an id and then I could just make a single setLabel(labelId, newLabel); script.
"I also would like to be able to pre-load default values in some of the answers from the calling Java app. In fact, I'd like to set up all sorts of things from the calling app -- set some questions visible, enabled, populate templates' radio-button values, etc." I've also been thinking about this myself. Right now there is only one way to build a survey. It expects an XML configuration that implements SurveyReader. To provide all this functionality you're talking about I was thinking about making a new type of SurveyReader that you build in java. I haven't thought this through completely but maybe you'd interact with the interface like this:
JavaSurveyBuilder builder = new JavaSurveyBuilder(); //implements SurveyReader
builder.addPage().addQuestion("textId", "Text Label", TEXT_FIELD).addQuestion("rbId", "Radio Buttons Label", RADIO_BUTTONS, rbChoices).addQuestion(...);
builder.addPage()...
builder.addInitScript("my script")...
...
ClientSurvey survey = new Survey(builder);
...
"the general design doesn't seem to be very friendly towards interaction between the calling app and the survey other than via XML" Yes that is by design. I know that I will make api mistakes that I wish I could change later. To make this easier on me, I default to making any class/method for internal use only so that I may change it later without having to worry about breaking someone else's code. Do you think the JavaSurveyBuilder I described above will be sufficient for your needs?
"the 'Question' class has facilities for things like a validationListener, but no 'answerChangedListener' (that I can see) " I'm a little confused. The Question class has a list of AnswerListener. Is that what you were looking for? If not, I don't understand the question.
"Forgive my ignorance of beanshell, but is there any way to call methods in my host Java code from the script?" That's an interesting request I never considered before. In a sense, I'm already doing that. If you look at src\org\jsurveylib\model\script\interpreter\ScriptInterpreter.java#setupEnvironment, you'll see that's what this method is doing. The interpreter.set method adds the survey instance to the command's scope so they can call the survey later.
"Furthermore, while the combination of the built-in question-types with beanshell is incredibly powerful, it would be nice to be able to embed a Java component of my own as an answer-type, for example a date-field that can pop up a calendar-style day-picker; there seems to be no provisions made for this, short of heavily hacking into the library itself." Yes, there are no provisions made for this. I considered this at one point but decided to not make it possible. I can't remember all of my reasoning, but I know one important reason is it would prevent me from changing the api later. If I could add some extremely powerful functionality by adding a parameter to a method, I didn't want to be held back by the fact that it may break some other library's code. Also, you really do need to know a lot about the library to add a new question type. I believe it touches every layer of the app. You'd need to add your own renderer and figure out a way to update XMLSurveyReader too. So, in summary, it wouldn't be trivial. That, and the fact that jsurveylib is new enough that it could really benefit by a date picker like the one you described being added to it directly as opposed to an outside add-on.
"Nonetheless, if I can access the "INTERNAL ONLY" classes and methods, combined with some script, I might be able to cobble up something usable, so... while not recommended, does that seem a workable solution, or is there a pitfall that will stop me?" The pitfall to that is that the next version of jsurveylib may change the api in a way so that your code won't compile. If you don't plan on upgrading to the next jsurveylib, it shouldn't effect you. If you don't mind changing your code after you upgrade, it shouldn't effect you.
"I've only got precious little time to do the whole app, so I'd just as soon not have to code up my own survey module as well as the rest, at least for the initial release." I understand your issues. As I've mentioned, most of these feature requests are already planned for in the future. Unfortunately, none of them are required for what I use jsurveylib for at my day job so I can not say when they will be added. BTW, what other survey/questionnaire libraries have you looked at? I hope this has helped, let me know if you have any other questions.