Package org.jugile.daims

Source Code of org.jugile.daims.Bo

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) {}
    }   
  }

}
TOP

Related Classes of org.jugile.daims.Bo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.