package org.openrosa.client.xforms;
import org.openrosa.client.model.OptionDef;
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.NamedNodeMap;
import com.google.gwt.xml.client.Node;
import com.google.gwt.xml.client.NodeList;
import com.google.gwt.xml.client.XMLParser;
/**
* Utility methods used when manipulating xforms xml documents.
* This class is different from XmlUtil by handling xforms
* specifics in the xml document. This class does not have any
* xforms object model classes like FormDef, PageDef, QuestionDef, etc.
*
* @author daniel
*
*/
public class XformUtil {
/**
* All methods in this class are static and hence we expect no external
* Instantiation of this class.
*/
private XformUtil(){
}
/**
* Removes extraneous whitespace between words
* (ie " mary had a little lamb "
* becomes "mary had a little lamb")
* @param s
* @return cleansed String
*/
public static String ripOutWhitespace(String s){
String[] arr = s.split(" ");
String newS = "";
for(int i=0;i<arr.length;i++){
if(!arr[i].isEmpty()){
newS+=" "+arr[i];
}
}
newS = newS.trim();
return newS;
}
/**
* Creates a node from an xml fragment.
*
* @param xml the xml fragment.
* @return the node.
*/
public static Element getNode(String xml){
xml = "<xf:xforms xmlns:xf=\"http://www.w3.org/2002/xforms\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:h=\"http://www.w3.org/1999/xhtml\">" + xml;
xml = xml + "</xf:xforms>";
Document doc = XMLParser.parse(xml);
Element node = (Element)doc.getDocumentElement().getChildNodes().item(0);
if(node.getAttribute(XformConstants.ATTRIBUTE_NAME_XMLNS) != null)
node.removeAttribute(XformConstants.ATTRIBUTE_NAME_XMLNS);
return node;
}
/**
* Renames a node.
*
* @param node the node to rename.
* @param newName the new node name.
* @return the new renamed node.
*/
public static Element renameNode(Element node, String newName){
String xml = node.toString();
xml = xml.replace(node.getNodeName(), newName);
Element child = getNode(xml);
Element parent = (Element)node.getParentNode();
parent.replaceChild(child, node);
return child;
}
/**
* Gets an xforms instance node with a given id.
*
* @param modelNode the xforms model node.
* @param instanceId the instance id.
* @return the instance node.
*/
public static Element getInstanceNode(Element modelNode, String instanceId){
NodeList nodes = modelNode.getElementsByTagName(XformConstants.NODE_NAME_INSTANCE); //TODO What if we have a different prefix from xf?
if(nodes.getLength() == 0)
nodes = modelNode.getElementsByTagName(XformConstants.NODE_NAME_INSTANCE_MINUS_PREFIX);
if(nodes == null)
return null;
for(int index = 0; index < nodes.getLength(); index++){
Element node = (Element)nodes.item(index);
if(instanceId.equalsIgnoreCase(node.getAttribute(XformConstants.ATTRIBUTE_NAME_ID)))
return node;
}
return null;
}
/**
* Gets the model node of an xforms document with a given root node.
*
* @param element the xforms document root node.
* @return the mode node.
*/
public static Element getModelNode(Element element){
int numOfEntries = element.getChildNodes().getLength();
for (int i = 0; i < numOfEntries; i++) {
if (element.getChildNodes().item(i).getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element)element.getChildNodes().item(i);
//String tagname = getNodeName(child);
String tagname = child.getNodeName(); //NODE_NAME_INSTANCE has prefix
//if (tagname.equals(NODE_NAME_MODEL)||tagname.equals(NODE_NAME_MODEL_MINUS_PREFIX))
if(XmlUtil.nodeNameEquals(tagname,XformConstants.NODE_NAME_MODEL_MINUS_PREFIX))
return child;
else{
child = getModelNode(child);
if(child != null)
return child;
}
}
}
return null;
}
/**
* Copies the xforms model instance from one document to another.
*
* @param srcDoc the xforms document with the model instance to copy.
* @param destDoc the xforms document whose model instance we are to replace with the copied one.
*/
public static void copyModel(Document srcDoc, Document destDoc){
if(srcDoc != null){
Element oldModel = getModelNode(srcDoc.getDocumentElement());
Element newModel = getModelNode(destDoc.getDocumentElement());
if(oldModel != null && newModel != null){
oldModel = (Element)oldModel.cloneNode(true);
destDoc.importNode(oldModel, true);
newModel.getParentNode().appendChild(oldModel);
newModel.getParentNode().removeChild(newModel);
}
}
}
/**
* Gets a meaningful label to display to the user to identify this option
* @param od
* @return
*/
public static String getOptionDefIdentifierString(OptionDef od){
return od.getQuestionID().isEmpty() ? od.getItextId() : od.getQuestionID();
}
/**
* Gets the node that has the text value which is the answer, in a given
* binding or variable name (e.g obs/weight/value)
*
* @param dataNode the xforms instance data node.
* @param variableName te binding or variable name.
* @return the value node.
*/
public static Element getValueNode(Element dataNode,String variableName){
Element node = dataNode;
int startPos = 0;
int endPos = variableName.indexOf('/');
if(endPos < 0)
return null;
while(endPos >= 0){
String name = variableName.substring(startPos, endPos);
node = XmlUtil.getNode(node,name);
if(node == null)
return null;
startPos = endPos + 1;
endPos = variableName.indexOf('/', startPos);
}
String name = variableName.substring(startPos);
node = XmlUtil.getNode(node,name);
return node;
}
/**
* Changes our namespace prefix to the one used in the original document.
*
* @param doc the document.
* @param xml the document xml.
* @return the new document xml with all namespace prefixes equal to the ones in the original document.
*/
public static String normalizeNameSpace(Document doc, String xml){
//We use xf prefix. So if the loaded xform
//did not use the same prefix, then replace our prefix with that
//which came with the xform.
String prefix = doc.getDocumentElement().getPrefix();
if(!"xf".equals(prefix)){
if(prefix == null || prefix.trim().length() == 0)
prefix = "";
else
prefix += ":";
xml = xml.replace("xf:", prefix);
}
return xml;
}
/**
* Gets the instance node of an xforms document.
*
* @param doc the xforms document.
* @return the instance node.
*/
public static Element getInstanceNode(Document doc){
return getInstanceNode(doc.getDocumentElement());
}
/**
* Gets the instance data node of an xforms document.
*
* @param doc the xforms document.
* @return the instance data node.
*/
public static Element getInstanceDataNode(Document doc){
return getInstanceDataNode(getInstanceNode(doc));
}
/**
* Gets the instance data node of an xforms document as a new document.
*
* @param doc the xforms document.
* @return the new document having the instance data node as its root node.
*/
public static Document getInstanceDataDoc(Document doc){
Element data = getInstanceDataNode(getInstanceNode(doc));
Document dataDoc = XMLParser.createDocument();
dataDoc.appendChild(dataDoc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\""));
dataDoc.appendChild(data.cloneNode(true));
Element root = dataDoc.getDocumentElement();
NamedNodeMap attributes = doc.getDocumentElement().getAttributes();
for(int index = 0; index < attributes.getLength(); index++){
Node attribute = attributes.item(index);
String name = attribute.getNodeName();
if(name.startsWith("xmlns:")){
try{
root.setAttribute(name, attribute.getNodeValue());
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
return dataDoc;
}
/**
* Gets the instance node of an xforms document with a given root node.
*
* @param element the root node of the xforms document.
* @return the instance node.
*/
public static Element getInstanceNode(Element element){
int numOfEntries = element.getChildNodes().getLength();
for (int i = 0; i < numOfEntries; i++) {
if (element.getChildNodes().item(i).getNodeType() == Node.ELEMENT_NODE) {
Element child = (Element)element.getChildNodes().item(i);
//String tagname = getNodeName(child);
String tagname = child.getNodeName(); //NODE_NAME_INSTANCE has prefix
//if(tagname.equals(NODE_NAME_INSTANCE)||tagname.equals(NODE_NAME_INSTANCE_MINUS_PREFIX))
if(XmlUtil.nodeNameEquals(tagname,XformConstants.NODE_NAME_INSTANCE_MINUS_PREFIX))
return child;
else{
child = getInstanceNode(child);
if(child != null)
return child;
}
}
}
return null;
}
/**
* Gets the instance data node of an xforms document with a given root node.
*
* @param element the xforms document root node.
* @return the instance data node.
*/
public static Element getInstanceDataNode(Element element){
int numOfEntries = element.getChildNodes().getLength();
for (int i = 0; i < numOfEntries; i++) {
if (element.getChildNodes().item(i).getNodeType() == Node.ELEMENT_NODE)
return (Element)element.getChildNodes().item(i);
}
return null;
}
/**
* Hardcoded here for now. Returns the xmlns:jrm attribute value for the data node in instance node.
* @return
*/
public static String getDataXMLNSjrm(){
return "http://dev.commcarehq.org/jr/xforms";
}
}