/* * 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.util.restorable; import java.util.Date; import java.util.Vector; import org.openrosa.client.jr.core.model.Constants; import org.openrosa.client.jr.core.model.data.DateData; import org.openrosa.client.jr.core.model.data.DateTimeData; import org.openrosa.client.jr.core.model.data.DecimalData; import org.openrosa.client.jr.core.model.data.IAnswerData; import org.openrosa.client.jr.core.model.data.IntegerData; import org.openrosa.client.jr.core.model.data.SelectMultiData; import org.openrosa.client.jr.core.model.data.StringData; import org.openrosa.client.jr.core.model.data.TimeData; import org.openrosa.client.jr.core.model.instance.FormInstance; import org.openrosa.client.jr.core.model.instance.TreeElement; import org.openrosa.client.jr.core.model.instance.TreeReference; import org.openrosa.client.jr.core.services.storage.IStorageIterator; import org.openrosa.client.jr.core.services.storage.IStorageUtility; import org.openrosa.client.jr.core.services.storage.Persistable; import org.openrosa.client.jr.core.services.transport.payload.ByteArrayPayload; import org.openrosa.client.jr.core.util.externalizable.Externalizable; public class RestoreUtils { public static final String RECORD_ID_TAG = "rec-id"; public static IXFormyFactory xfFact; public static TreeReference ref (String refStr) { return xfFact.ref(refStr); } public static TreeReference absRef (String refStr, FormInstance dm) { TreeReference ref = ref(refStr); if (!ref.isAbsolute()) { ref = ref.parent(topRef(dm)); } return ref; } public static TreeReference topRef (FormInstance dm) { return ref("/" + dm.getRoot().getName()); } public static TreeReference childRef (String childPath, TreeReference parentRef) { return ref(childPath).parent(parentRef); } private static FormInstance newDataModel (String topTag) { FormInstance dm = new FormInstance(); dm.addNode(ref("/" + topTag)); return dm; } public static FormInstance createDataModel (Restorable r) { FormInstance dm = newDataModel(r.getRestorableType()); if (r instanceof Persistable) { addData(dm, RECORD_ID_TAG, new Integer(((Persistable)r).getID())); } return dm; } public static FormInstance createRootDataModel (Restorable r) { FormInstance inst = createDataModel(r); inst.schema = "http://openrosa.org/backup"; addData(inst, "timestamp", new Date(), Constants.DATATYPE_DATE_TIME); return inst; } public static void addData (FormInstance dm, String xpath, Object data) { addData(dm, xpath, data, getDataType(data)); } public static void addData (FormInstance dm, String xpath, Object data, int dataType) { if (data == null) { dataType = -1; } IAnswerData val; switch (dataType) { case -1: val = null; break; case Constants.DATATYPE_TEXT: val = new StringData((String)data); break; case Constants.DATATYPE_INTEGER: val = new IntegerData((Integer)data); break; case Constants.DATATYPE_DECIMAL: val = new DecimalData((Double)data); break; case Constants.DATATYPE_BOOLEAN: val = new StringData(((Boolean)data).booleanValue() ? "t" : "f"); break; case Constants.DATATYPE_DATE: val = new DateData((Date)data); break; case Constants.DATATYPE_DATE_TIME: val = new DateTimeData((Date)data); break; case Constants.DATATYPE_TIME: val = new TimeData((Date)data); break; case Constants.DATATYPE_CHOICE_LIST: val = (SelectMultiData)data; break; default: throw new IllegalArgumentException("Don't know how to handle data type [" + dataType + "]"); } TreeReference ref = absRef(xpath, dm); if (dm.addNode(ref, val, dataType) == null) { throw new RuntimeException("error setting value during object backup [" + xpath + "]"); } } //used for outgoing data public static int getDataType (Object o) { int dataType = -1; if (o instanceof String) { dataType = Constants.DATATYPE_TEXT; } else if (o instanceof Integer || o instanceof Long) { dataType = Constants.DATATYPE_INTEGER; } else if (o instanceof Float || o instanceof Double) { dataType = Constants.DATATYPE_DECIMAL; } else if (o instanceof Date) { dataType = Constants.DATATYPE_DATE; } else if (o instanceof Boolean) { dataType = Constants.DATATYPE_BOOLEAN; //booleans are serialized as a literal 't'/'f' } else if (o instanceof SelectMultiData) { dataType = Constants.DATATYPE_CHOICE_LIST; } return dataType; } //used for incoming data public static int getDataType (Class c) { int dataType; if (c == String.class) { dataType = Constants.DATATYPE_TEXT; } else if (c == Integer.class || c == Long.class) { dataType = Constants.DATATYPE_INTEGER; } else if (c == Float.class || c == Double.class) { dataType = Constants.DATATYPE_DECIMAL; } else if (c == Date.class) { dataType = Constants.DATATYPE_DATE; //Clayton Sims - Jun 16, 2009 - How are we handling Date v. Time v. DateTime? } else if (c == Boolean.class) { dataType = Constants.DATATYPE_TEXT; //booleans are serialized as a literal 't'/'f' } else { throw new RuntimeException("Can't handle data type " + c.getName()); } return dataType; } public static Object getValue (String xpath, FormInstance tree) { return getValue(xpath, topRef(tree), tree); } public static Object getValue (String xpath, TreeReference context, FormInstance tree) { TreeElement node = tree.resolveReference(ref(xpath).contextualize(context)); if (node == null) { throw new RuntimeException("Could not find node [" + xpath + "] when parsing saved instance!"); } if (node.isRelevant()) { IAnswerData val = node.getValue(); return (val == null ? null : val.getValue()); } else { return null; } } public static void applyDataType (FormInstance dm, String path, TreeReference parent, Class type) { applyDataType(dm, path, parent, getDataType(type)); } public static void applyDataType (FormInstance dm, String path, TreeReference parent, int dataType) { TreeReference ref = childRef(path, parent); Vector v = dm.expandReference(ref); for (int i = 0; i < v.size(); i++) { TreeElement e = dm.resolveReference((TreeReference)v.elementAt(i)); e.dataType = dataType; } } public static void templateChild (FormInstance dm, String prefixPath, TreeReference parent, Restorable r) { TreeReference childRef = (prefixPath == null ? parent : RestoreUtils.childRef(prefixPath, parent)); childRef = childRef(r.getRestorableType(), childRef); templateData(r, dm, childRef); } public static void templateData (Restorable r, FormInstance dm, TreeReference parent) { if (parent == null) { parent = topRef(dm); applyDataType(dm, "timestamp", parent, Date.class); } if (r instanceof Persistable) { applyDataType(dm, RECORD_ID_TAG, parent, Integer.class); } r.templateData(dm, parent); } public static void mergeDataModel (FormInstance parent, FormInstance child, String xpathParent) { mergeDataModel(parent, child, absRef(xpathParent, parent)); } public static void mergeDataModel (FormInstance parent, FormInstance child, TreeReference parentRef) { TreeElement parentNode = parent.resolveReference(parentRef); //ugly if (parentNode == null) { parentRef = parent.addNode(parentRef); parentNode = parent.resolveReference(parentRef); } TreeElement childNode = child.getRoot(); int mult = parentNode.getChildMultiplicity(childNode.getName()); childNode.setMult(mult); parentNode.addChild(childNode); } /*public static FormInstance exportRMS (IStorageUtility storage, Class type, String parentTag, IRecordFilter filter) { if (!Externalizable.class.isAssignableFrom(type) || !Restorable.class.isAssignableFrom(type)) { return null; } FormInstance dm = newDataModel(parentTag); IStorageIterator ri = storage.iterate(); while (ri.hasMore()) { Object obj = ri.nextRecord(); if (filter == null || filter.filter(obj)) { FormInstance objModel = ((Restorable)obj).exportData(); mergeDataModel(dm, objModel, topRef(dm)); } } return dm; }*/ public static FormInstance subDataModel (TreeElement top) { TreeElement newTop = top.shallowCopy(); newTop.setMult(0); return new FormInstance(newTop); } /*public static void exportRMS (FormInstance parent, Class type, String grouperName, IStorageUtility storage, IRecordFilter filter) { FormInstance entities = RestoreUtils.exportRMS(storage, type, grouperName, filter); RestoreUtils.mergeDataModel(parent, entities, "."); }*/ /*public static void importRMS (FormInstance dm, IStorageUtility storage, Class type, String path) { if (!Externalizable.class.isAssignableFrom(type) || !Restorable.class.isAssignableFrom(type)) { return; } boolean idMatters = Persistable.class.isAssignableFrom(type); String childName = ((Restorable)PrototypeFactory.getInstance(type)).getRestorableType(); TreeElement e = dm.resolveReference(absRef(path, dm)); Vector children = e.getChildrenWithName(childName); for (int i = 0; i < children.size(); i++) { FormInstance child = subDataModel((TreeElement)children.elementAt(i)); Restorable inst = (Restorable)PrototypeFactory.getInstance(type); //restore record id first so 'importData' has access to it int recID = -1; if (idMatters) { recID = ((Integer)getValue(RECORD_ID_TAG, child)).intValue(); ((Persistable)inst).setID(recID); } inst.importData(child); try { if (idMatters) { storage.write((Persistable)inst); } else { storage.add((Externalizable)inst); } } catch (Exception ex) { throw new RuntimeException("Error importing RMS during restore! [" + type.getName() + ":" + recID + "]; " + ex.getMessage()); } } }*/ public static ByteArrayPayload dispatch (FormInstance dm) { return (ByteArrayPayload)xfFact.serializeInstance(dm); } public static FormInstance receive (byte[] payload, Class restorableType) { return xfFact.parseRestore(payload, restorableType); } public static boolean getBoolean (Object o) { if (o instanceof String) { String bool = (String)o; if ("t".equals(bool)) { return true; } else if ("f".equals(bool)) { return false; } else { throw new RuntimeException("boolean string must be t or f"); } } else { throw new RuntimeException("booleans are encoded as strings"); } } }