package com.radicaldynamic.groupinform.xform; import java.util.Iterator; import java.util.regex.Pattern; import android.util.Log; import com.radicaldynamic.groupinform.application.Collect; /* * Used for labels and hints on fields and item labels * * This is class is responsible for looking up itext translations at run time */ public class FieldText { private final String t = "FieldText: "; // If the value is null then ref should be non-null and refer to a translation ID private String value = null; // Regular string value associated with this text object private String ref = null; // A reference to an itext translation ID /* * Used by Field class to supply a default empty FieldText (which will hopefully be replaced upon form parsing) */ public FieldText() { value = ""; } /* * This constructor is intended to be used by FormReader when it * parses and initialises labels & hints from recursivelyApplyProperty() */ public FieldText(String valueOrRef) { if (Collect.Log.VERBOSE) Log.v(Collect.LOGTAG, t + "instantiating with " + valueOrRef); if (Pattern.matches("^jr:.*", valueOrRef)) { String [] items = valueOrRef.split("'"); // jr:itext('GroupLabel') String id = items[1]; // The string between the single quotes ref = id; value = null; } else { // Store these values as-is (they should be pre-encoded in the XML) value = valueOrRef; ref = null; } } // Takes a human readable string and encodes it for XMLs public void setValue(String value) { if (value == null) this.value = value; else this.value = encodeXMLEntities(value); } // Returns a decoded string ready for human consumption public String getValue() { return decodeXMLEntities(value); } public void setRef(String ref) { this.ref = ref; } public String getRef() { return ref; } // See http://en.wikipedia.org/wiki/Character_encodings_in_HTML#XML_character_references static public String decodeXMLEntities(String str) { str = str.replaceAll("&", "&"); str = str.replaceAll("'", "'"); str = str.replaceAll(">", ">"); str = str.replaceAll("<", "<"); str = str.replaceAll(""", "\""); return str; } // Encoding with special support to ensure that <output value="/widgets/regex"/> is not encoded static public String encodeXMLEntities(String str) { str = str.replaceAll("&", "&"); str = str.replaceAll("'", "'"); str = str.replaceAll("(?<!\"/)>", ">"); str = str.replaceAll("(?!<output)<", "<"); str = str.replaceAll("(?!value=\"|\"\\s*/)\"", """); return str; } public boolean isTranslated() { return ref != null && ref.length() > 0 ? true : false; } @Override public String toString() { String result = "[Default Translation Missing]"; if (value == null) { /* * If this FieldText has a reference to an itext translation then obtain * a single translation to represent this field on the form builder screen. * * FIXME: We should select the most appropriate language (not necessarily English) * before falling back to English. The most appropriate language can be determined * by checking the locale of the device. Right now we just pick the first available language. */ if (ref == null) { if (Collect.Log.WARN) Log.w(Collect.LOGTAG, t + "field has neither value nor reference to itext translation"); } else { String translation = getDefaultTranslation(ref); if (translation instanceof String && translation.length() > 0) result = translation; } } else { result = getValue(); } return result; } /* * Retrieve a default translation for a specific ID */ private String getDefaultTranslation(String id) { Iterator<Translation> translations = Collect.getInstance().getFormBuilderState().getTranslations().iterator(); while (translations.hasNext()) { Translation t = translations.next(); if (t.isFallback()) { Iterator<Translation> x = t.getTexts().iterator(); while (x.hasNext()) { Translation text = x.next(); if (text.getId().equals(id)) return text.getValue(); } } } return ""; } }