de.bund.bfr.knime.pmm.nodes/src/de/bund/bfr/knime/pmm/manualmodelconf/ManualModelEditorNodeDialog.java
/*******************************************************************************
* Copyright (c) 2015 Federal Institute for Risk Assessment (BfR), Germany
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contributors:
* Department Biological Safety - BfR
*******************************************************************************/
package de.bund.bfr.knime.pmm.manualmodelconf;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.HashSet;
import javax.swing.JOptionPane;
import org.hsh.bfr.db.DBKernel;
import org.javers.core.Javers;
import org.javers.core.JaversBuilder;
import org.javers.core.diff.Diff;
import org.javers.core.diff.changetype.NewObject;
import org.javers.core.diff.changetype.ObjectRemoved;
import org.javers.core.diff.changetype.ValueChange;
import org.jdom2.JDOMException;
import org.knime.core.data.DataTableSpec;
import org.knime.core.node.BufferedDataTable;
import org.knime.core.node.DataAwareNodeDialogPane;
import org.knime.core.node.InvalidSettingsException;
import org.knime.core.node.NodeSettingsRO;
import org.knime.core.node.NodeSettingsWO;
import org.knime.core.node.NotConfigurableException;
import de.bund.bfr.knime.pmm.common.DepXml;
import de.bund.bfr.knime.pmm.common.ParametricModel;
import de.bund.bfr.knime.pmm.common.PmmException;
import de.bund.bfr.knime.pmm.common.PmmTimeSeries;
import de.bund.bfr.knime.pmm.common.PmmXmlDoc;
import de.bund.bfr.knime.pmm.common.PmmXmlElementConvertable;
import de.bund.bfr.knime.pmm.common.XmlConverter;
import de.bund.bfr.knime.pmm.common.generictablemodel.KnimeRelationReader;
import de.bund.bfr.knime.pmm.common.generictablemodel.KnimeSchema;
import de.bund.bfr.knime.pmm.common.generictablemodel.KnimeTuple;
import de.bund.bfr.knime.pmm.common.pmmtablemodel.Model1Schema;
import de.bund.bfr.knime.pmm.common.pmmtablemodel.Model2Schema;
import de.bund.bfr.knime.pmm.common.pmmtablemodel.TimeSeriesSchema;
import de.bund.bfr.knime.pmm.manualmodelconf.ui.MMC_M;
import de.bund.bfr.knime.pmm.manualmodelconf.ui.MMC_TS;
/**
* <code>NodeDialog</code> for the "ManualModelConf" Node.
*
* @author ManualModelConf
*/
public class ManualModelEditorNodeDialog extends DataAwareNodeDialogPane {
private MMC_M m_mmcm;
private MMC_TS m_mmcts;
private HashMap<Integer, HashSet<Integer>> oneStepFitTs;
PmmXmlDoc inputDoc = null;
/**
* New pane for configuring the ManualModelConf node.
*/
protected ManualModelEditorNodeDialog() {
try {
m_mmcts = new MMC_TS();
m_mmcm = new MMC_M(JOptionPane.getRootFrame(), 1, "", false, m_mmcts);
m_mmcm.setConnection(DBKernel.getLocalConn(true));
oneStepFitTs = new HashMap<>();
this.addTab("Model Definition", m_mmcm);
//this.addTab("Microbial Data", m_mmcts);
}
catch( Exception e ) {
e.printStackTrace( System.err );
}
}
@Override
protected void saveSettingsTo(final NodeSettingsWO settings)
throws InvalidSettingsException {
//m_confui.stopCellEditing();
m_mmcm.stopCellEditing();
//settings.addString( ManualModelConfNodeModel.PARAM_XMLSTRING, m_confui.toXmlString() );
HashMap<Integer, HashMap<String, Object[]>> diffMap = getDiff(inputDoc, m_mmcm.listToDoc());
settings.addString( ManualModelConfNodeModel.PARAM_XMLDIFFSTRING, XmlConverter.objectToXml(diffMap));
String xml = m_mmcm.listToXmlString();
settings.addString( ManualModelConfNodeModel.PARAM_XMLSTRING, xml);
String tStr = m_mmcm.tssToXmlString();
settings.addString( ManualModelConfNodeModel.PARAM_TSXMLSTRING, tStr );//-1673022417
settings.addString(ManualModelConfNodeModel.PARAM_TSONESTEP, XmlConverter.objectToXml(oneStepFitTs));
}
@Override
protected void loadSettingsFrom(NodeSettingsRO settings,
BufferedDataTable[] inData) throws NotConfigurableException {
String mStr = null;
String tsStr = null;
// OneStepFitTss
try {
if (settings.containsKey(ManualModelConfNodeModel.PARAM_TSONESTEP)) {
oneStepFitTs = XmlConverter.xmlToObject(settings.getString(ManualModelConfNodeModel.PARAM_TSONESTEP), new HashMap<Integer, HashSet<Integer>>());
}
}
catch( InvalidSettingsException e ) {
e.printStackTrace();
}
// MMC_M
try {
if (settings.containsKey(ManualModelConfNodeModel.PARAM_XMLSTRING)) {
mStr = settings.getString(ManualModelConfNodeModel.PARAM_XMLSTRING);
}
}
catch( InvalidSettingsException e ) {
e.printStackTrace();
}
//MMC_TS
try {
if (settings.containsKey(ManualModelConfNodeModel.PARAM_TSXMLSTRING)) {
tsStr = settings.getString(ManualModelConfNodeModel.PARAM_TSXMLSTRING);
}
}
catch (Exception e) {} // e.printStackTrace();
inputDoc = null;
if (inData != null && inData.length == 1) {
DataTableSpec inSpec = inData[0].getDataTableSpec();
try {
KnimeSchema tsSchema = new TimeSeriesSchema();
KnimeSchema inSchema1 = new Model1Schema();
KnimeSchema inSchema2 = new Model2Schema();
boolean hasTs = tsSchema.conforms(inSpec);
boolean hasM1 = inSchema1.conforms(inSpec);
boolean hasM2 = inSchema2.conforms(inSpec);
KnimeSchema finalSchema = null;
if (hasM1 && hasM2) finalSchema = KnimeSchema.merge(inSchema1, inSchema2);
else if (hasM1) finalSchema = inSchema1;
else if (hasM2) finalSchema = inSchema2;
if (hasTs) finalSchema = (finalSchema == null) ? tsSchema : KnimeSchema.merge(tsSchema, finalSchema);
if (finalSchema != null) {
KnimeRelationReader reader = new KnimeRelationReader(finalSchema, inData[0]);
HashMap<Integer, PmmTimeSeries> tss = new HashMap<>();
HashMap<Integer, ParametricModel> m1s = new HashMap<>();
HashMap<Integer, ParametricModel> m2s = new HashMap<>();
HashMap<ParametricModel, HashMap<String, ParametricModel>> m_secondaryModels = new HashMap<>();
Integer condID = null;
Integer m1EstID = null, m2EstID;
while (reader.hasMoreElements()) {
KnimeTuple row = reader.nextElement();
if (hasTs) {
PmmTimeSeries ts = new PmmTimeSeries(row);
condID = ts.getCondId();
//System.err.println(condID);
tss.put(condID, ts);
}
if (hasM1) {
ParametricModel pm1 = new ParametricModel(row, 1, hasTs ? condID : null);
m1EstID = pm1.estModelId;
if (!m1s.containsKey(m1EstID)) {
m1s.put(m1EstID, pm1);
}
if (!oneStepFitTs.containsKey(m1EstID)) oneStepFitTs.put(m1EstID, new HashSet<Integer>());
HashSet<Integer> hs = oneStepFitTs.get(m1EstID);
hs.add(pm1.condId);
if (hasM2) {
ParametricModel pm2 = new ParametricModel(row, 2, null);
m2EstID = pm2.estModelId;
if (!m2s.containsKey(m2EstID)) {
m2s.put(m2EstID, pm2);
}
if (!m_secondaryModels.containsKey(m1s.get(m1EstID))) m_secondaryModels.put(m1s.get(m1EstID), new HashMap<String, ParametricModel>());
HashMap<String, ParametricModel> hm = m_secondaryModels.get(m1s.get(m1EstID));
hm.put(pm2.getDepVar(), m2s.get(m2EstID));
}
}
else if (hasM2) {
ParametricModel pm2 = new ParametricModel(row, 2, null);
m2EstID = pm2.estModelId;
if (!m2s.containsKey(m2EstID)) {
m2s.put(m2EstID, pm2);
}
m_secondaryModels.put(null, new HashMap<String, ParametricModel>());
HashMap<String, ParametricModel> hm = m_secondaryModels.get(null);
hm.put(pm2.getDepVar(), m2s.get(m2EstID));
}
}
m_mmcm.setInputData(m1s.values(), m_secondaryModels, tss);
try {
inputDoc = m_mmcm.listToDoc().clonePMs();
} catch (InvalidSettingsException e) {
e.printStackTrace();
}
try {
if (settings.containsKey(ManualModelConfNodeModel.PARAM_XMLDIFFSTRING)) { // new behaviour
HashMap<Integer, HashMap<String, Object[]>> diffMap = XmlConverter.xmlToObject(settings.getString(ManualModelConfNodeModel.PARAM_XMLDIFFSTRING), new HashMap<Integer, HashMap<String, Object[]>>());
for (Integer id : diffMap.keySet()) {
HashMap<String, Object[]> hm = diffMap.get(id);
if (m1s.containsKey(id)) {
for (String property : hm.keySet()) {
Object[] o = hm.get(property);
if (o != null) setValue(m1s.get(id), property, o[0], o[1]);
}
}
else { // secondary Models
checkSec(m_secondaryModels, id, hm);
}
}
}
else {
try {
if (mStr != null && !mStr.isEmpty()) {
PmmXmlDoc mDoc = new PmmXmlDoc(mStr);
for (int i = 0; i < mDoc.size(); i++) {
PmmXmlElementConvertable el = mDoc.get(i);
if (el instanceof ParametricModel) {
ParametricModel pm = (ParametricModel) el;
if (m1s.containsKey(pm.estModelId)) m1s.put(pm.estModelId, pm);
else {
checkSec(m_secondaryModels, pm);
}
}
}
}
if (tsStr != null && !tsStr.isEmpty()) {
PmmXmlDoc tsDoc = new PmmXmlDoc(tsStr);
for (int i = 0; i < tsDoc.size(); i++) {
PmmXmlElementConvertable el = tsDoc.get(i);
if (el instanceof PmmTimeSeries) {
PmmTimeSeries ts = (PmmTimeSeries) el;
if (tss.containsKey(ts.getCondId())) tss.put(ts.getCondId(), ts);
}
}
}
}
catch (IOException e) {
e.printStackTrace();
}
catch (JDOMException e) {
e.printStackTrace();
}
}
m_mmcm.setInputData(m1s.values(), m_secondaryModels, tss);
}
catch( InvalidSettingsException e ) {
e.printStackTrace();
}
}
}
catch (PmmException e) {}
}
else {
if (tsStr != null) m_mmcts.setTS(tsStr);
if (mStr != null) m_mmcm.setFromXmlString(mStr);
}
}
private void checkSec(HashMap<ParametricModel, HashMap<String, ParametricModel>> m_secondaryModels, ParametricModel pm) {
boolean idFound = false;
for (HashMap<String, ParametricModel> sm : m_secondaryModels.values()) {
for (String dep : sm.keySet()) {
ParametricModel pms = sm.get(dep);
if (pms.estModelId == pm.estModelId) {
idFound = true;
sm.put(dep, pm);
break;
}
}
if (idFound) break;
}
}
private void checkSec(HashMap<ParametricModel, HashMap<String, ParametricModel>> m_secondaryModels, int id, HashMap<String, Object[]> hm) {
boolean idFound = false;
for (HashMap<String, ParametricModel> sm : m_secondaryModels.values()) {
for (ParametricModel pm : sm.values()) {
if (pm.estModelId == id) {
idFound = true;
for (String property : hm.keySet()) {
Object[] o = hm.get(property);
if (o != null) setValue(pm, property, o[0], o[1]);
}
break;
}
}
if (idFound) break;
}
}
private HashMap<Integer, HashMap<String, Object[]>> getDiff(PmmXmlDoc inputDoc, PmmXmlDoc outputDoc) {
HashMap<Integer, HashMap<String, Object[]>> result = new HashMap<>();
Javers javers = JaversBuilder.javers().build();
/*
Diff diff = javers.compare(inputDoc, outputDoc);
System.out.println(diff);
*/
for (PmmXmlElementConvertable out : outputDoc.getElementSet()) {
if (out instanceof ParametricModel) {
ParametricModel mOut = (ParametricModel) out;
for (PmmXmlElementConvertable in : inputDoc.getElementSet()) {
if (in instanceof ParametricModel) {
ParametricModel mIn = (ParametricModel) in;
if (mOut.estModelId == mIn.estModelId) {
Diff diff = javers.compare(mIn, mOut);
if (diff.getChanges().size() > 0) {
System.out.println(mOut.estModelId + "\n" + diff);
for (ValueChange c : diff.getChangesByType(ValueChange.class)) {
//System.out.println(c.getProperty().getName());
//System.out.println(c.getLeft());
//System.out.println(c.getRight());
if (!result.containsKey(mOut.estModelId)) result.put(mOut.estModelId, new HashMap<String, Object[]>());
HashMap<String, Object[]> hm = result.get(mOut.estModelId);
String gid = c.getAffectedGlobalId().value();
if (gid.indexOf("#") > 0) gid = gid.substring(gid.indexOf("#") + 1) + "#"; // parameter#elementSet/2#
else gid = "";
hm.put(gid + c.getProperty().getName(), new Object[]{c.getLeft(),c.getRight()});
/*
System.out.println(c);
System.out.println(mIn.getFittedModelName());
setValue(mIn, c.getProperty().getName(), c.getRight());
System.out.println(mIn.getFittedModelName());
*/
}
for (NewObject c : diff.getChangesByType(NewObject.class)) {
if (!result.containsKey(mOut.estModelId)) result.put(mOut.estModelId, new HashMap<String, Object[]>());
HashMap<String, Object[]> hm = result.get(mOut.estModelId);
String gid = c.getAffectedGlobalId().value();
if (gid.indexOf("#") > 0) gid = gid.substring(gid.indexOf("#") + 1) + "#"; // estLit#elementSet/0#
else {
System.err.println(gid);
//gid = "";
}
hm.put(gid, new Object[]{null,c.getAffectedObject().get()});
}
for (ObjectRemoved c : diff.getChangesByType(ObjectRemoved.class)) {
if (!result.containsKey(mOut.estModelId)) result.put(mOut.estModelId, new HashMap<String, Object[]>());
HashMap<String, Object[]> hm = result.get(mOut.estModelId);
String gid = c.getAffectedGlobalId().value();
if (gid.indexOf("#") > 0) gid = gid.substring(gid.indexOf("#") + 1) + "#"; // estLit#elementSet/0#
else {
System.err.println(gid);
//gid = "";
}
hm.put(gid, new Object[]{c.getAffectedObject().get(), null});
}
}
break;
}
}
}
}
}
return result;
}
private void setValue(Object o, String fieldName, Object oldValue, Object newValue) {
// todo: checken, ob "oldValue = oldValue"?
try {
if (fieldName.indexOf("#") >= 0) { // z.B. '#parameter#elementSet/2'
String fn = fieldName.substring(0, fieldName.indexOf("#"));// "parameter"
String param = fieldName.substring(fieldName.lastIndexOf("#") + 1); // max
Field f = o.getClass().getDeclaredField(fn);
if (f.getType() == PmmXmlDoc.class) {
f.setAccessible(true);
PmmXmlDoc doc = (PmmXmlDoc) f.get(o);
String indexS = fieldName.substring(fieldName.indexOf("#elementSet/") + "#elementSet/".length(), fieldName.lastIndexOf("#"));// 2
Integer index = Integer.parseInt(indexS);
PmmXmlElementConvertable pxec = (index < doc.getElementSet().size()) ? doc.get(index) : null;
if (newValue == null && oldValue instanceof PmmXmlElementConvertable) { // ObjectRemoved
PmmXmlElementConvertable pxecO = (PmmXmlElementConvertable) oldValue;
Javers javers = JaversBuilder.javers().build();
for (PmmXmlElementConvertable pxecL : doc.getElementSet()) {
Diff diff = javers.compare(pxecO, pxecL);
if (diff.getChanges().size() == 0) {
doc.remove(pxecL);
break;
}
}
}
else if (oldValue == null && newValue instanceof PmmXmlElementConvertable) { // NewObject
doc.add((PmmXmlElementConvertable) newValue);
}
else {
Field field = pxec.getClass().getDeclaredField(param);
field.setAccessible(true);
field.set(pxec, newValue);
}
}
else if (f.getType() == DepXml.class) {
f.setAccessible(true);
DepXml dx = (DepXml) f.get(o);
Field field = dx.getClass().getDeclaredField(param);
field.setAccessible(true);
field.set(dx, newValue);
}
else {
System.out.println(f.getType());
}
}
else {
Field field = o.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(o, newValue);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}