/*
* Copyright (C) 2009 JavaRosa
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.openrosa.client.jr.core.model;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import org.openrosa.client.java.io.DataInputStream;
import org.openrosa.client.java.io.DataOutputStream;
import org.openrosa.client.jr.core.model.utils.DateUtils;
import org.openrosa.client.jr.core.services.locale.Localizable;
import org.openrosa.client.jr.core.services.locale.Localizer;
import org.openrosa.client.jr.core.util.externalizable.DeserializationException;
import org.openrosa.client.jr.core.util.externalizable.ExtUtil;
import org.openrosa.client.jr.core.util.externalizable.ExtWrapList;
import org.openrosa.client.jr.core.util.externalizable.ExtWrapNullable;
import org.openrosa.client.jr.core.util.externalizable.ExtWrapTagged;
import org.openrosa.client.jr.core.util.externalizable.PrototypeFactory;
/**
* The definition of a Question to be presented to users when
* filling out a form.
*
* QuestionDef requires that any IDataReferences that are used
* are contained in the FormDefRMS's PrototypeFactoryDeprecated in order
* to be properly deserialized. If they aren't, an exception
* will be thrown at the time of deserialization.
*
* @author Daniel Kayiwa/Drew Roos
*
*/
public class QuestionDef implements IFormElement, Localizable {
private int id;
private IDataReference binding; /** reference to a location in the model to store data in */
private int controlType; /* The type of widget. eg TextInput,Slider,List etc. */
private String appearanceAttr;
private String helpTextID;
private String labelInnerText;
private String helpText;
private String textID; /* The id (ref) pointing to the localized values of (pic-URIs,audio-URIs,text) */
private Vector<SelectChoice> choices;
private ItemsetBinding dynamicChoices;
Vector observers;
public QuestionDef () {
this(Constants.NULL_ID, Constants.DATATYPE_TEXT);
}
public QuestionDef (int id, int controlType) {
setID(id);
setControlType(controlType);
observers = new Vector();
}
public int getID () {
return id;
}
public void setID (int id) {
this.id = id;
}
public IDataReference getBind() {
return binding;
}
public void setBind(IDataReference binding) {
this.binding = binding;
}
public int getControlType() {
return controlType;
}
public void setControlType(int controlType) {
this.controlType = controlType;
}
public String getAppearanceAttr () {
return appearanceAttr;
}
public void setAppearanceAttr (String appearanceAttr) {
this.appearanceAttr = appearanceAttr;
}
/**
* Only if there is no localizable version of the <hint> available should this method be used
*/
public String getHelpText () {
return helpText;
}
/**
* Only if there is no localizable version of the <hint>available should this method be used
*/
public void setHelpText (String helpText) {
this.helpText = helpText;
}
public String getHelpTextID () {
return helpTextID;
}
public void setHelpTextID (String textID) {
this.helpTextID = textID;
}
public void addSelectChoice (SelectChoice choice) {
if (choices == null) {
choices = new Vector<SelectChoice>();
}
choice.setIndex(choices.size());
choices.addElement(choice);
}
public void removeSelectChoice(SelectChoice choice){
if(choices == null) {
choice.setIndex(0);
return;
}
if(choices.contains(choice)){
choices.removeElement(choice);
}
}
public void removeAllSelectChoices(){
if(choices != null){
choices.removeAllElements();
}
}
public Vector<SelectChoice> getChoices () {
return choices;
}
public SelectChoice getChoice (int i) {
return choices.elementAt(i);
}
public int getNumChoices () {
return (choices != null ? choices.size() : 0);
}
public SelectChoice getChoiceForValue (String value) {
for (int i = 0; i < getNumChoices(); i++) {
if (getChoice(i).getValue().equals(value)) {
return getChoice(i);
}
}
return null;
}
public ItemsetBinding getDynamicChoices () {
return dynamicChoices;
}
public void setDynamicChoices (ItemsetBinding ib) {
if (ib != null) {
ib.setDestRef(this);
}
this.dynamicChoices = ib;
}
/**
* true if the answer to this question yields xml tree data, not a simple string value
*/
public boolean isComplex () {
return (dynamicChoices != null && dynamicChoices.copyMode);
}
//Deprecated
public void localeChanged(String locale, Localizer localizer) {
if (choices != null) {
for (int i = 0; i < choices.size(); i++) {
choices.elementAt(i).localeChanged(null, localizer);
}
}
if (dynamicChoices != null) {
dynamicChoices.localeChanged(locale, localizer);
}
alertStateObservers(FormElementStateListener.CHANGE_LOCALE);
}
public Vector getChildren () {
return null;
}
public void setChildren (Vector v) {
throw new IllegalStateException();
}
public void addChild (IFormElement fe) {
throw new IllegalStateException();
}
public IFormElement getChild (int i) {
return null;
}
/*
* (non-Javadoc)
* @see org.javarosa.core.util.Externalizable#readExternal(java.io.DataInputStream)
*/
public void readExternal(DataInputStream dis, PrototypeFactory pf) throws IOException, DeserializationException {
setID(ExtUtil.readInt(dis));
binding = (IDataReference)ExtUtil.read(dis, new ExtWrapNullable(new ExtWrapTagged()), pf);
setAppearanceAttr((String)ExtUtil.read(dis, new ExtWrapNullable(String.class), pf));
setTextID((String)ExtUtil.read(dis, new ExtWrapNullable(String.class), pf));
setLabelInnerText((String)ExtUtil.read(dis, new ExtWrapNullable(String.class), pf));
setHelpText((String)ExtUtil.read(dis, new ExtWrapNullable(String.class), pf));
setHelpTextID((String)ExtUtil.read(dis, new ExtWrapNullable(String.class), pf));
setControlType(ExtUtil.readInt(dis));
choices = ExtUtil.nullIfEmpty((Vector)ExtUtil.read(dis, new ExtWrapList(SelectChoice.class), pf));
for (int i = 0; i < getNumChoices(); i++) {
choices.elementAt(i).setIndex(i);
}
setDynamicChoices((ItemsetBinding)ExtUtil.read(dis, new ExtWrapNullable(ItemsetBinding.class)));
}
/*
* (non-Javadoc)
* @see org.javarosa.core.util.Externalizable#writeExternal(java.io.DataOutputStream)
*/
public void writeExternal(DataOutputStream dos) throws IOException {
ExtUtil.writeNumeric(dos, getID());
ExtUtil.write(dos, new ExtWrapNullable(binding == null ? null : new ExtWrapTagged(binding)));
ExtUtil.write(dos, new ExtWrapNullable(getAppearanceAttr()));
ExtUtil.write(dos, new ExtWrapNullable(getTextID()));
ExtUtil.write(dos, new ExtWrapNullable(getLabelInnerText()));
ExtUtil.write(dos, new ExtWrapNullable(getHelpText()));
ExtUtil.write(dos, new ExtWrapNullable(getHelpTextID()));
ExtUtil.writeNumeric(dos, getControlType());
ExtUtil.write(dos, new ExtWrapList(ExtUtil.emptyIfNull(choices)));
ExtUtil.write(dos, new ExtWrapNullable(dynamicChoices));
}
/* === MANAGING OBSERVERS === */
public void registerStateObserver (FormElementStateListener qsl) {
if (!observers.contains(qsl)) {
observers.addElement(qsl);
}
}
public void unregisterStateObserver (FormElementStateListener qsl) {
observers.removeElement(qsl);
}
public void unregisterAll () {
observers.removeAllElements();
}
public void alertStateObservers (int changeFlags) {
for (Enumeration e = observers.elements(); e.hasMoreElements(); )
((FormElementStateListener)e.nextElement()).formElementStateChanged(this, changeFlags);
}
/*
* (non-Javadoc)
* @see org.javarosa.core.model.IFormElement#getDeepChildCount()
*/
public int getDeepChildCount() {
return 1;
}
public void setLabelInnerText(String labelInnerText) {
this.labelInnerText = labelInnerText;
}
public String getLabelInnerText() {
return labelInnerText;
}
public String getTextID() {
return textID;
}
public void setTextID(String textID) {
if(DateUtils.stringContains(textID,";")){
System.err.println("Warning: TextID contains ;form modifier:: \""+textID.substring(textID.indexOf(";"))+"\"... will be stripped.");
textID=textID.substring(0, textID.indexOf(";")); //trim away the form specifier
}
this.textID = textID;
}
}