package org.jugile.daims;
/*
Copyright (C) 2011-2011 Jukka Rahkonen email: jukka.rahkonen@iki.fi
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
//import gnu.trove.iterator.TLongObjectIterator;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.apache.log4j.Logger;
import org.jugile.daims.anno.Connection1N;
import org.jugile.daims.anno.ConnectionN1;
import org.jugile.daims.anno.ConnectionNN;
import org.jugile.daims.anno.DaimsObject;
import org.jugile.daims.anno.Fld;
import org.jugile.util.Buffer;
import org.jugile.util.DBConnection;
import org.jugile.util.DBPool;
import org.jugile.util.DOH;
import org.jugile.util.EnumType;
import org.jugile.util.HiLo;
import org.jugile.util.IDO;
import org.jugile.util.IDomain;
import org.jugile.util.Jugile;
import org.jugile.util.Money;
import org.jugile.util.Proxy;
import org.jugile.util.Time;
/**
* <i>"this is verse"</i> <b>()</b>
*
* <br/>==========<br/>
*
* here is doc
*
* @author jukka.rahkonen@iki.fi
*
*/
public class Bo extends Jugile implements Cloneable, Comparable<Bo>, IDO {
protected static Logger log = Logger.getLogger(Bo.class);
private UnitOfWork uow() { return DomainCore.getUnitOfWork(); }
// ----------- id & version & equals -------------
private long id;
public long id() { return id; }
public String getId() { return ""+id; }
/**
* Create "old" means creating already existing object: it has an id already.
* This is the case when object is imported from delta or from initial import.
* Returns an object that was born old.
*/
protected static Bo createOld(Class<? extends Bo> cl, long id) {
Bo o = getInstance(cl);
o.id = id;
return o;
}
/**
* Creates a new virgin object and aquires new id for it from idpool.
*/
protected static Bo createNew(Class<? extends Bo> cl) {
Bo o = getInstance(cl);
o.id = HiLo.nextid();
o.isNew = true;
return o;
}
private static Bo getInstance(Class<? extends Bo> cl) {
try { return cl.newInstance();
} catch (Exception e) { fail(e); return null; }
}
private long version;
protected long version() { return version; }
private void incrVersion() { version++; }
public long getVersion() { return version; }
private Bo origin;
protected boolean isOrigin() { return origin == null; }
protected boolean isCopy() { return origin != null; }
private boolean modified = false;
protected boolean isModified() { return modified; }
private boolean isNew = false;
protected boolean isNew() { return isNew; }
protected void setIsNew(boolean v) { isNew = v; }
private boolean isArchived = false;
public boolean isArchived() { return isArchived; }
protected boolean isRef() { return version < 0; }
private Bo createRef() {
Bo o = Bo.createOld(this.getClass(), id);
o.version = -1;
o.origin = this;
return o;
}
protected Bo copy(BoInfo bi) {
if (uow().isReadOnly()) return this;
if (isCopy()) fail("tried to copy a copy");
// no cloning for to avoid cloning automatically all referenced objects (Bo member fields)
// create empty copied object
Bo o = Bo.createOld(this.getClass(), id);
return copy(bi,o);
}
protected Bo copy(BoInfo bi, Bo o) {
o.version = version;
o._modified = _modified;
try {
// copy fields
for (Field f : bi.flds) {
f.set(o, f.get(this));
}
// copy Bo n-1 refs
for (Field f : bi.cn1s) {
Bo bo = (Bo)f.get(this);
if (bo != null) {
f.set(o, bo.createRef());
}
}
// copy all collections
for (Field f : bi.cols) {
// create BoMapDelta from BoMap
BoCollection<Bo> bc = (BoCollection<Bo>)f.get(this);
BoCollection<Bo> bc2 = (BoCollection)f.getType().newInstance();
bc2.setOrigin(bc);
f.set(o,bc2);
}
} catch (Exception e) { fail(e); }
if (origin == null) o.origin = this;
return o;
}
/**
* Gets the version which is in uow or creates a copy.
* @return uow copy.
*/
protected Bo getUowCopy() {
return uow().get(getClass(), id);
}
protected BoMap<Bo> getBoMap(String name) throws Exception {
Field f = fld(name);
BoCollection<Bo> bc = (BoCollection<Bo>)f.get(this);
return bc.origin;
}
protected BoCollection<Bo> getBoCollection(String name) throws Exception {
Field f = fld(name);
BoCollection<Bo> bc = (BoCollection<Bo>)f.get(this);
return bc;
}
public int hashCode() { return Long.valueOf(id).hashCode(); }
public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof Bo)) return false;
return ((Bo)o).id == id;
}
public int compareTo(Bo o) {
if (o == null) return 1;
return getId().compareTo(o.getId());
}
// ----------- flds ------------
protected Bo setFld(String name, Object value) {
try {
Field f = fld(name);
f.set(this, value);
modified = true;
_modified = now();
} catch(Exception e) { fail(e); }
return this;
}
protected Bo getN1(String name) {
try {
Field f = fld(name);
Bo value = (Bo)f.get(this);
if (value == null) return null;
if (value.isRef()) {
// lazy load
Bo val = uow().get((Class<Bo>)value.getClass(), value.id);
f.set(this, val);
return val;
}
return value;
} catch (Exception e) { fail(e); }
return null;
}
protected Bo setN1(String name, Bo o) {
try {
Field f = fld(name);
setDeltaN1(f,o); // set n-1 collection
modified = true;
_modified = now();
} catch(Exception e) { fail(e); }
return this;
}
private void setDeltaN1(Field f, Bo o) throws Exception {
// remove this from old index
//log.debug("setDeltaN1: " + f.getName() + " o: " + o);
Bo oldvalue = (Bo)f.get(this);
if (oldvalue != null) {
// for lazyloaded objects
if (oldvalue.isRef()) {
oldvalue = uow().get((Class<Bo>)oldvalue.getClass(), oldvalue.id);
}
}
if (eq(oldvalue,o)) {
//log.debug("oldvalue eq o: " + o);
return; // no change
}
if (oldvalue != null) {
// must use unit of work here to get collection
oldvalue = uow().get((Class<Bo>)oldvalue.getClass(), oldvalue.id);
// if removed in uow -> oldvalue is now null
if (oldvalue != null) {
BoCollection<Bo> bc = oldvalue.getBoCollection(f.getAnnotation(ConnectionN1.class).c());
bc.remove(this,false);
//String status = bc.status();
}
//log.debug("deleted o from: " + bc);
}
// add this into new index
if (o != null) {
BoCollection<Bo> bc = o.getBoCollection(f.getAnnotation(ConnectionN1.class).c());
bc.add(this);
}
// set ref field value
f.set(this,o);
}
private void setOriginN1(Field f, Bo o) throws Exception {
if (!isOrigin()) fail("tried to setOriginN1 to copy");
if (o != null) if (!o.isOrigin()) fail("tried to add copy to origin");
//log.debug("setOriginN1: "+ toStr(f) + o + "this: " +this);
// remove this from old index
Bo oldvalue = (Bo)f.get(this);
if (eq(oldvalue,o)) {
//log.debug("oldvalue eq o " + this);
return; // no change
}
if (oldvalue != null) {
BoMap<Bo> bm = oldvalue.getBoMap(f.getAnnotation(ConnectionN1.class).c());
//log.debug("removing from other end: " + id + " bm: " +bm);
bm.remove(id);
}
// add this into new index
if (o != null) {
BoMap bm = o.getBoMap(f.getAnnotation(ConnectionN1.class).c());
bm.add(this);
}
// set ref field value
f.set(this,o);
//log.debug("setOriginN1 ok: " +o + "this: " + this);
}
protected BoCollection getAll(String name) {
try {
Field f = fld(name);
BoCollection value = (BoCollection)f.get(this);
value.reset(); // remove old queries and iterator
//log.debug("getAll from bomap: " + value.origin + "this: " + this);
return value;
} catch (Exception e) { fail(e); }
return null;
}
protected Bo add(String name, Bo o) {
if (o == null) return this;
if (o.isOrigin()) {
log.warn("tried to add origin: " + o);
o = uow().get(o.getClass(), o.id);
}
try {
Field f = fld(name);
Connection1N c = f.getAnnotation(Connection1N.class);
if (c != null) {
o.setN1(c.o(), this); // use other end setter
return this;
}
ConnectionNN nn = f.getAnnotation(ConnectionNN.class);
if (nn != null) {
((BoCollection<Bo>)f.get(this)).add(o);
o.getBoCollection(nn.c()).add(this); // update other end index
return this;
}
} catch(Exception e) { fail(e); }
return this;
}
protected void remove(String name, Bo o) {
if (o == null) return;
if (o.isOrigin()) {
log.warn("tried to remove origin: " + o);
o = uow().get(o.getClass(), o.id);
}
try {
Field f = fld(name);
Connection1N c = f.getAnnotation(Connection1N.class);
if (c != null) {
//log.debug("remove 1N by calling o.setN1");
o.setN1(c.o(), null); // use other end setter and set null
return;
}
ConnectionNN nn = f.getAnnotation(ConnectionNN.class);
if (nn != null) {
//log.debug("remove NN: " + o + " " + name);
((BoCollection<Bo>)f.get(this)).remove(o,false);
o.getBoCollection(nn.c()).remove(this,false); // update other end index
return;
}
} catch(Exception e) { fail(e); }
return;
}
protected void cleanUpConnections() throws Exception {
//log.debug("cleanUp: " + this);
for (Field f : bi().cols) {
// foreach item do remove
BoCollection<Bo> bc = (BoCollection<Bo>)f.get(this);
//log.debug("cleanUp: " + f.getName() + " "+bc);
for (Bo o : bc) {
// TODO: optimize this
// get it to uow
Bo uo = uow().get(o.getClass(), o.id());
remove(f.getName(),uo); // cleans up other end
}
}
for (Field f : bi().cn1s) {
setN1(f.getName(), null);
}
}
protected void cleanUpOriginConnections() throws Exception {
//log.debug("cleanUpOrigin: " + this);
for (Field f : bi().nns) {
BoCollection<Bo> bc = (BoCollection<Bo>)f.get(this);
for (Bo o : bc) {
removeOriginNN(f.getName(), o.id);
}
}
for (Field f : bi().c1ns) {
BoCollection<Bo> bc = (BoCollection<Bo>)f.get(this);
String oname = f.getAnnotation(Connection1N.class).o();
for (Bo o : bc) {
o.origin.setFld(oname,null);
}
}
for (Field f : bi().cn1s) {
//log.debug("set null: " + f.getName());
setOriginN1(f, null);
}
}
protected void addOriginNN(String name, Bo o) {
if (o == null) return;
try {
Field f = fld(name);
ConnectionNN nn = f.getAnnotation(ConnectionNN.class);
if (nn == null) fail("not a nn collection");
getBoMap(name).add(o);
o.getBoMap(nn.c()).add(this); // update other end index
} catch(Exception e) { fail(e); }
}
protected void removeOriginNN(String name, long id) {
try {
//log.debug("removeOriginNN: " + name + " id: " + id);
Field f = fld(name);
ConnectionNN nn = f.getAnnotation(ConnectionNN.class);
if (nn == null) fail("not a nn collection");
Bo o = getBoMap(name).remove(id);
if (o != null) o.getBoMap(nn.c()).remove(this); // update other end index
} catch(Exception e) { fail(e); }
}
protected Class getOtherEndClass(String name) {
try {
Field f = fld(name);
ConnectionNN nn = f.getAnnotation(ConnectionNN.class);
if (nn == null) fail("not a nn collection");
int l = "Collection".length();
String cname = f.getType().getName();
cname = cname.substring(0, cname.length()-l);
return getClassByName(cname);
} catch(Exception e) { fail(e); return null; }
}
enum FldType { FIELD, CN1, C1N, NN, COL }
protected List<Field> flds() { return flds(FldType.FIELD); }
protected List<Field> cn1s() { return flds(FldType.CN1); }
protected List<Field> c1ns() { return flds(FldType.C1N); }
protected List<Field> nns() { return flds(FldType.NN); }
protected List<Field> cols() { return flds(FldType.COL); }
protected List<Field> flds(FldType type) {
List<Field> res = new ArrayList<Field>();
Class cl = this.getClass().getSuperclass();
for (Field f : cl.getDeclaredFields()) {
if (type == FldType.FIELD) {
Fld fa = f.getAnnotation(Fld.class);
if (fa == null) continue;
}
if (type == FldType.CN1) {
ConnectionN1 c = f.getAnnotation(ConnectionN1.class);
if (c == null) continue;
}
if (type == FldType.C1N) {
Connection1N c = f.getAnnotation(Connection1N.class);
if (c == null) continue;
}
if (type == FldType.NN) {
ConnectionNN c = f.getAnnotation(ConnectionNN.class);
if (c == null || !c.first()) continue;
//if (c == null || c.first()) continue;
}
if (type == FldType.COL) {
Connection1N c = f.getAnnotation(Connection1N.class);
ConnectionNN nn = f.getAnnotation(ConnectionNN.class);
if (c == null && nn == null) continue;
}
f.setAccessible(true);
res.add(f);
}
return res;
}
private Field fld(String name) throws Exception {
Field f = this.getClass().getSuperclass().getDeclaredField(name);
f.setAccessible(true);
return f;
}
// ------------ csv ---------------
protected final static String TSFORMAT = "yyyyMMdd-HHmmss.SSS";
protected final static char CSVDELIMITER = ',';
protected final static char NULLCHAR = '-';
protected final static String DEL = "D";
protected final static String HEADERSTART = "#";
protected final static String MAPSTART = "@";
public String toCsv() { return toCsv(0);}
private final String toCsv(int versionIncrement) {
Buffer buf = new Buffer();
buf.add(getId()); // id
buf.add(CSVDELIMITER);
buf.add(""+(version+versionIncrement)); // version
buf.add(CSVDELIMITER);
buf.add(_modified==null?"-":_modified.toString(TSFORMAT)); // modified time
List<Field> flds = flds();
flds.addAll(cn1s());
for (Field f : flds) {
buf.add(CSVDELIMITER);
Object o = null;
try { o = f.get(this); } catch (Exception e) { fail(e); }
if (o == null) buf.add(NULLCHAR);
else if (o instanceof String) {
if (((String)o).length() == 0) buf.add(NULLCHAR);
else if (((String)o).equals(""+NULLCHAR)) {
buf.add(""+NULLCHAR+NULLCHAR);
}
else {
String str = Jugile.replaceControls((String)o);
str = escape(str,CSVDELIMITER);
buf.add(str);
}
}
else if (o instanceof Bo) buf.add(((Bo)o).getId());
else if (o instanceof Time) buf.add(((Time)o).toString(TSFORMAT));
else if (o instanceof EnumType) buf.add(""+((EnumType)o).getValue());
else buf.add(escape(o.toString(),CSVDELIMITER));
}
return buf.toString();
}
protected final void parse(DomainData dd, List<String> flds, List<String> values) throws Exception {
if (!isOrigin()) fail("tried to parse into copy");
// skip id(0)
long v = parseLongSafe(values.get(1)); // verify version - don't update older version
if (version > 0) if (v < version) return;
version = v;
if ("-".equals(values.get(2))) _modified = null;
else _modified = new Time(values.get(2),TSFORMAT);
for (int i = 3; i < flds.size(); i++) {
String val = null;
try {
val = values.get(i);
} catch (Exception e) {
log.error("ERROR: " + e);
log.error(" " + join(flds) + " = " + join(values));
}
//log.debug("parse " + id() +": "+ flds.get(i) + "=" + val);
if (empty(val)) continue;
String fld = flds.get(i);
Field f = fld(fld);
if (val.equals(""+NULLCHAR)) val = null;
Object o = null;
if (f.getAnnotation(Fld.class) != null) {
if (val != null) {
if (f.getType() == Time.class) {
o = new Time(val,TSFORMAT);
} else if (f.getType() == Money.class) {
o = new Money(parseIntSafe(val));
} else if (f.getType() == String.class) {
if (val.equals(""+NULLCHAR+NULLCHAR)) val = ""+NULLCHAR;
o = val;
} else if (f.getType() == int.class) {
o = parseIntSafe(val);
} else if (f.getType() == boolean.class) {
o = parseBoolSafe(val);
} else if (f.getType() == double.class) {
o = parseDoubleSafe(val);
} else if (hasInterface(f.getType(),EnumType.class)) {
o = Proxy.staticCall(f.getType(), "map", val);
} else {
fail("unknown type: " + f.getType());
}
} else {
if (f.getType() == int.class) o = 0;
else if (f.getType() == double.class) o = 0.0;
}
f.set(this,o);
} else if (f.getAnnotation(ConnectionN1.class) != null) {
Bo bo = dd.getOrCreate((Class<Bo>)f.getType(), val);
//log.debug("setting OriginN1: " + toStr(f) + bo + "this: " + this);
this.setOriginN1(f, bo);
} else {
fail("invalid field: " + f);
}
}
// TODO: this causes ANG loadCsv to work incorrectly
if (dd != null && dd.d() != null)
onLoad((IDomain)dd.d()); // call onLoad
}
private boolean hasInterface(Class cl, Class ic) {
for (Class i : cl.getInterfaces()) {
if (i.equals(ic)) return true;
}
return false;
}
protected final String _headerRow() {
Buffer buf = new Buffer();
buf.add(Bo.HEADERSTART);
buf.add(getClass().getName());
buf.add(Bo.CSVDELIMITER);
buf.add("v");
buf.add(Bo.CSVDELIMITER);
buf.add("ts");
List<Field> flds = flds();
flds.addAll(cn1s());
for (Field f : flds) {
buf.add(Bo.CSVDELIMITER);
buf.add(f.getName());
}
return buf.toString();
}
protected final String _deleteRow() {
// version incremented also here, version is incremented in delta batch
//return DEL + CSVDELIMITER + id + CSVDELIMITER + (version+1);
return DEL + CSVDELIMITER + id + CSVDELIMITER + version;
}
protected String _delta() {
// TODO: optimize: calculate real delta - only modified fields
onSave(); // callback, TODO: make this call separately before delta() round
return toCsv(1); // increment version in delta
}
protected String _nnHeader(Field f) {
String clName = getClass().getName();
String cn1 = f.getName();
//String cn2 = f.getAnnotation(ConnectionNN.class).c();
return MAPSTART + CSVDELIMITER + clName + CSVDELIMITER + cn1;
}
public String toString() {
// TODO: truncate long text fields
String res = "[Bo "+ getClassName(this) + " "+ hex(super.hashCode()) + " "+ toCsv()+"] ";
if (origin != null) res += "origin-> " + origin.toString();
return res;
}
public static String toStr(Field f) {
return "[Fld " + f.getName() +":" + getClassName(f.getType()) +"] ";
}
protected void dump(Buffer buf) throws Exception {
// 5430D082 Person (2,1,0,-,p1,-)
// family: 5430D082 Family (2,1,0) -> 5430D082
buf.ln(toRefStr());
buf.incrIndent();
for (Field f : cn1s()) {
Bo bo = (Bo)f.get(this);
buf.ln(f.getName() +": " + (bo==null?"null":bo.toRefStr()));
}
// persons( 5430D082 -> 5430D082 ):
// items: 5430D082, 5430D082, 5430D082, 5430D082
// deleted: 5430D082, 5430D082
for (Field f : cols()) {
BoCollection dm = (BoCollection<Bo>)f.get(this);
BoMap orig = dm.origin;
String ref1 = hex(dm.hashCode());
String ref2 = hex(orig.hashCode());
buf.ln(f.getName() + "( " + ref1 + " -> " + ref2 + " ):");
buf.incrIndent();
if (isOrigin()) {
orig.dumpShort(buf);
} else {
dm.dumpShort(buf);
}
buf.decrIndent();
}
buf.decrIndent();
}
protected String toRefStr() {
// 5430D082 Family (2,1,0) -> 5430D082
String res = realHash();
res += " " + getClassName(this)+" ("+toCsv()+") ";
if (origin != null) res += " -> " + origin.toRefStr();
return res;
}
protected String realHash() { return hex(super.hashCode()); }
// ---- db -----
private List<String> _dbflds() {
List<String> res = new ArrayList<String>();
for (Field f : flds()) {
String name = f.getAnnotation(Fld.class).name();
if (empty(name)) name = f.getName() + "_f";
res.add(name);
}
for (Field f : cn1s()) {
String name = f.getAnnotation(ConnectionN1.class).name();
if (empty(name)) name = f.getName() + "_f";
res.add(name);
}
return res;
}
private final static String sHeaderFlds[] = {
"id_f","ts","host","vers","archived"
};
private int countSelectHeaderFlds() {
return sHeaderFlds.length;
}
private String getSelectHeaderFldsString() {
return Jugile.join(Arrays.asList(sHeaderFlds), ",");
}
protected String _getSelectFlds() {
Buffer buf = new Buffer();
buf.add(getSelectHeaderFldsString());
for (String fld : _dbflds()) {
buf.add(","+fld);
}
return buf.toString();
}
protected String _getUpdateFlds() {
Buffer buf = new Buffer();
for (int i = 0; i < _dbflds().size(); i++) {
if (i > 0) buf.add(",");
buf.add(_dbflds().get(i)+"=?");
}
return buf.toString();
}
protected void _setWriteParams(DBConnection c, BoInfo bi) throws Exception {
int i = 0;
for (Field f : bi.flds) {
int size = bi.sizes.get(i);
if (size > 0) c.param(trim((String)f.get(this),size));
else c.param(f.get(this));
i++;
}
for (Field f : bi.cn1s) {
c.param(f.get(this)); // Bo can be reference or loaded
}
}
private Time _modified;
public Time getModifiedTime() { return _modified; }
protected void _setState(DomainData dd, BoInfo bi, List<Object> row) throws Exception {
_modified = row.get(1)==null?null:new Time((java.util.Date)row.get(1));
//String host = (String)row.get(2);
version = (Integer)row.get(3);
_setFlds(dd,bi,row,countSelectHeaderFlds());
}
/**
* Read current version from db immediately.
*/
public void refresh() {
DBPool db = DBPool.getPool();
DBConnection c = db.getConnection();
DomainData dd = DomainCore.cd();
try {
c.prepare("select "+_getSelectFlds()+" from "+table() +" where id_f=?");
c.param(id());
List<List> rows = c.select();
if (rows.size() == 1) {
List row = rows.get(0);
// set state
Bo o = origin;
o._modified = row.get(1)==null?null:new Time((java.util.Date)row.get(1));
//String host = (String)row.get(2);
o.version = (Integer)row.get(3);
o._setFlds(dd,bi(),row,countSelectHeaderFlds());
// copy fields to copy
o.copy(bi(),this);
}
c.commit();
} catch (Exception e) {
try { c.rollback(); fail(e); } catch (Exception e2) { fail(e2); }
} finally {
try { c.free(); } catch (Exception e2) { fail(e2); }
}
}
/**
* Write to db immediately.
*/
public void flush() {
DBPool db = DBPool.getPool();
DBConnection c = db.getConnection();
try {
this._dbWriteFlds(c, bi());
c.commit();
modified = false; // not modified anymore
// _modified Time still remains
incrVersion();
} catch (Exception e) {
try { c.rollback(); fail(e); } catch (Exception e2) { fail(e2); }
} finally {
try { c.free(); } catch (Exception e2) { fail(e2); }
}
}
protected void _setFlds(DomainData dd, BoInfo bi, List row, int idx) throws Exception {
for (Field f : bi.flds) {
Object val = row.get(idx++);
if (f.getType() == Time.class) {
val = val==null?null:new Time((java.util.Date)val);
} else if (f.getType() == double.class) {
val = val==null?0.0:((BigDecimal)val).doubleValue();
} else if (f.getType() == Money.class) {
val = val==null?null:new Money((Integer)val);
} else if (f.getType() == boolean.class) {
val = val==null?false:val;
} else if (hasInterface(f.getType(),EnumType.class)) {
if (val != null) {
val = Proxy.staticCall(f.getType(), "map",(Integer)val);
}
}
f.set(this, val);
}
for (Field f : bi.cn1s) {
Object val = row.get(idx++);
if (val == null) {
setOriginN1(f,null);
} else {
long id = (Integer)val;
if (dd != null) {
Bo bo = dd.getOrCreate((Class<Bo>)f.getType(), id);
setOriginN1(f,bo);
}
}
}
if (dd != null) onLoad((IDomain)dd.d()); // call onLoad
}
protected String table() {
Class<?> cl = this.getClass().getSuperclass();
DaimsObject e = cl.getAnnotation(DaimsObject.class);
return e.table();
}
protected void _dbWriteFlds(DBConnection c, BoInfo bi) throws Exception {
// is it insert or update?
if (isNew()) {
//print("INSERT: " + this + " " + isNew() +","+isCopy()+","+isModified());
c.prepare("insert into "+ bi.table+" set id_f=?,host=?,vers=?,"
+ bi.updateFlds);
c.param(id());
c.param(DomainCore.getHostname());
c.param(1L);
_setWriteParams(c,bi);
c.execute();
version = 1L;
} else {
if (isModified()) {
//print("UPDATE: " + this);
c.prepare("update "+ bi.table+" set host=?,vers=?,"
+ bi.updateFlds + " where id_f=? AND vers=?");
c.param(DomainCore.getHostname());
c.param(version+1L); // increment db version
_setWriteParams(c,bi);
c.param(Long.parseLong(getId()));
c.param(version);
int res = c.execute();
if (res != 1) {
fail("object not found: " + bi.table + " " + getId() + " " + version);
}
}
// increment mem object versions when db commit is ok
}
}
protected void writeAllNNToDB(DBConnection c, BoInfo bi) throws Exception {
int i = 0;
for (Field f : bi.nns) {
String nntable = bi.nntables.get(i++);
BoMap<Bo> bm = this.getBoMap(f.getName());
long oid1 = id();
for (Bo bo : bm) {
long oid2 = bo.id();
//log.debug("writeNN to DB: " + oid1 + " " + oid2);
if (oid1 == 0 || oid2 == 0) fail("mapping has null value");
c.prepare("insert into " + nntable + " set o1=?,o2=?");
//c.param(oid1);
//c.param(oid2);
c.param(oid2); // SWAP - old ANG project needs this
c.param(oid1);
c.execute();
}
}
}
public void delete() {
onDelete();
uow().delete(this);
}
protected BoInfo bi() {
BoInfo bi = new BoInfo();
Bo o = this;
bi.table = o.table();
bi.updateFlds = o._getUpdateFlds();
bi.flds = o.flds();
bi.cn1s = o.cn1s();
bi.cols = o.cols();
bi.c1ns = o.c1ns();
bi.nns = o.nns();
for (Field f : bi.nns) {
bi.nntables.add(f.getAnnotation(ConnectionNN.class).table());
}
for (Field f : bi.flds) {
bi.sizes.add(f.getAnnotation(Fld.class).size());
}
return bi;
}
protected void stats(Stats st, BoInfo bi) {
for (Field f : bi.cols) {
try {
BoCollection bc = (BoCollection)f.get(this);
st.addCollStats((Class<Bo>)getClass(), f.getName(), bc.size());
} catch (Exception e) { fail(e); }
}
}
// ------ life cycle callbacks ------
protected void onDelete() {}
protected void onDeleteEvent() {}
protected void onLoad(IDomain d) {}
protected void onSave() {}
// ---------- IDO ------------
// TODO: clean this IDO away
public boolean equals(IDO o) { return equals((Object)o); }
public DOH getDoh() { return new DOH(this.getClass().getName(),getId()); }
public void delete(boolean ondelete) { }
public boolean matches(Object spec) { return false; }
public String txtIdx(String lang) { return ""; }
// ----- sql interface ------
public void archive() {
//setArchived(false);
isArchived = true;
uow().archive(this);
}
public void restore() {
setArchived(false);
refresh();
}
private void setArchived(boolean v) {
isArchived = v;
String a = "0";
if (v) a = "1";
String sql = "update " + table() + " set archived="+a+" where id_f="+getId();
DBPool db = DBPool.getPool();
DBConnection c = db.getConnection();
try {
c.update(sql);
c.commit();
} catch (Exception e) {
log.error("dbread failed",e);
try { c.rollback(); } catch (Exception e2) { fail(e2);}
} finally {
try { c.free(); } catch (Exception e) {}
}
}
}