package org.openrosa.client.util; import java.util.HashMap; import java.util.List; import org.openrosa.client.model.FormDef; import org.openrosa.client.model.ItextModel; import org.openrosa.client.xforms.XmlUtil; import com.extjs.gxt.ui.client.store.ListStore; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.Window; import com.google.gwt.xml.client.Document; import com.google.gwt.xml.client.Element; import com.google.gwt.xml.client.Node; import com.google.gwt.xml.client.NodeList; import com.google.gwt.xml.client.Text; import com.google.gwt.xml.client.XMLParser; /** * Parsing of a new XML (only the Itext section). * * Also provides tools for updating the FormDef using the Itext data stored * in Itext * */ public class ItextParser { /** * Parses an xform and sets the text of various nodes based on the current a locale * as represented by their itext ids. The translation element with the "default" attribute (default="") OR the first locale in the itext block * (if no default attribute is found) is the one taken as the default. * * @param xml the xforms xml. * @return the document where all itext refs are filled with text for a given locale. */ public static Document parse(String xml){ // System.out.println(xml); Itext.clearLocales(); Document doc = XmlUtil.getDocument(xml); //Check if we have an itext block in this xform. NodeList itext = doc.getElementsByTagName("itext"); if(itext == null || itext.getLength() == 0){ return doc; } //Check if we have any translations in this itext block. NodeList translations = ((Element)itext.item(0)).getElementsByTagName("translation"); if(translations == null || translations.getLength() == 0) return doc; for(int i=0; i<translations.getLength();i++){ Element translation = (Element)translations.item(i); String languageName = translation.getAttribute("lang"); ItextLocale language = Itext.getLocale(languageName); //creates a new locale (if it doesn't exist already) and adds it to the list in Itext if(translation.getAttribute("default")!=null){ Itext.setDefaultLocale(languageName); } NodeList textNodes = translation.getChildNodes(); for(int j=0; j<textNodes.getLength(); j++){ if(textNodes.item(j).getNodeType() != Node.ELEMENT_NODE) continue; Element text = (Element)textNodes.item(j); String id= text.getAttribute("id"); if(id == null) continue; //invalid javarosa xform xml at this point NodeList forms = text.getElementsByTagName("value"); for(int k=0; k<forms.getLength(); k++){ String fullID = id; Node valNode = forms.item(k); boolean tagNameEqualsValue = ((Element)valNode).getTagName().toLowerCase().equals("value"); if(!tagNameEqualsValue || XmlUtil.getItextTextValue(valNode) == null || XmlUtil.getItextTextValue(valNode).isEmpty()){ continue; //means invalid xform itext } String textform = ((Element)valNode).getAttribute("form"); if(textform != null){ fullID = id + ";" + textform; language.setTranslation(fullID, XmlUtil.getItextTextValue(valNode)); //some textform value }else{ if(fullID.contains("_hint")){ language.setTranslation(fullID.replace("_", ";"), XmlUtil.getItextTextValue(valNode)); }else{ language.setTranslation(fullID, XmlUtil.getItextTextValue(valNode)); //this is obviously the default value } } } } } return doc; } /** * Updates an xforms document (XML) itext block based on data stored in the Itext object. * * @param doc the xforms document. * @param formDef the form definition object. * @param list the gxt grid itext model. */ public static void updateItextBlock(FormDef formDef){ Document doc = formDef.getDoc(); Element modelNode = XmlUtil.getNode(doc.getDocumentElement(),"model"); assert(modelNode != null); //we must have a model in an xform. Element itextNode = XmlUtil.getNode(modelNode,"itext"); if(itextNode != null){ itextNode.getParentNode().removeChild(itextNode); } if(!Itext.hasItext()){ return; } List<ItextLocale> locales = Itext.locales; if(locales == null) return; //Houston we have a problem. Itext hasn't been intialized properly (or at all). itextNode = formDef.getDoc().createElement("itext"); modelNode.appendChild(itextNode); for(ItextLocale locale : locales){ if(locale.hasItext()){ createTextValueNodes(formDef,locale,itextNode); } } } /** * Creates the text (and value) nodes that fill up each translation block * @param formDef * @param list * @param locale */ private static void createTextValueNodes(FormDef formDef, ItextLocale locale, Element itextNode){ Document doc = formDef.getDoc(); Element translationNode = doc.createElement("translation"); translationNode.setAttribute("lang", locale.getName()); //Check for default if(locale.isDefault()){ translationNode.setAttribute("default", ""); } itextNode.appendChild(translationNode); for(String ItextID : locale.getAvailableItextIDs()){ String defaultText = locale.getDefaultTranslation(ItextID); //create nodes Element textNode = doc.createElement("text"); Element valueNode; //set default value itext value if it exists textNode.setAttribute("id", ItextID); if(defaultText != null){ valueNode = doc.createElement("value"); // valueNode.appendChild(doc.createTextNode(defaultText)); attachItextText(valueNode, defaultText); textNode.appendChild(valueNode); } //set/add special form values if they exist for(String form : locale.getAvailableForms(ItextID)){ if(locale.getTranslation(ItextID,form)!= null){ //interrupt for hint special case. if(form.toLowerCase().equals("hint")){ makeHintTextNode(translationNode, ItextID, locale.getTranslation(ItextID,form)); continue; } valueNode = doc.createElement("value"); valueNode.setAttribute("form", form); // valueNode.appendChild(doc.createTextNode(locale.getTranslation(ItextID,form))); attachItextText(valueNode, locale.getTranslation(ItextID,form)); textNode.appendChild(valueNode); } } //link nodes up translationNode.appendChild(textNode); } formDef.setDoc(doc); } /** * Creates the correct TEXT and (if necessary output nodes) combo and attaches * it to the specified parentNode. The parentNode is returned after attachment. * @param parentNode - Element you want to attach the Itext Text to. * @param rawText - Raw text (including any '<output>' string tags * @return */ public static Element attachItextText(Element parentNode, String rawText){ if(rawText == null || rawText.isEmpty()){ return null; }else{ rawText = "<foo xmlns=\"http://www.w3.org/2002/xforms\">" + rawText + "</foo>"; } Document tempDoc = XMLParser.parse(rawText); Node imp = parentNode.getOwnerDocument().importNode(tempDoc.getChildNodes().item(0), true); NodeList childNodes = imp.getChildNodes(); int numChildren = childNodes.getLength(); for(int i = 0; i<numChildren;i++){ Node child = imp.getFirstChild(); //for some reason appendChild below actively removes the child from the childNodes list... int nodeType = child.getNodeType(); parentNode.appendChild(child); } return parentNode; } /** * automatically creates a hint text node (for question hints) * The hint itext id becomes "itextID_hint" where itextID is specified as an * argument. * * Hint value is the actual text of the hint. * @param translation * @param itextID * @param hintValue */ private static void makeHintTextNode(Element translation,String itextID, String hintValue){ Document doc = translation.getOwnerDocument(); Element textNode = doc.createElement("text"); textNode.setAttribute("id", itextID+"_hint"); Element valNode = doc.createElement("value"); // valNode.appendChild(doc.createTextNode(hintValue)); attachItextText(valNode, hintValue); textNode.appendChild(valNode); translation.appendChild(textNode); } public static String getRowKeys(ItextModel row){ String k = ""; for(String key: row.getPropertyNames()){ k += key+","; } return k; } }