/*******************************************************************************
* Copyright 2011 Alex 'Ript' Malyshev <alexript@gmail.com>
*
* 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 net.autosauler.ballance.server.model;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import net.autosauler.ballance.server.mongodb.Database;
import net.autosauler.ballance.shared.datatypes.DataTypes;
import net.autosauler.ballance.shared.datatypes.StructValues;
import net.autosauler.ballance.shared.datatypes.Structure;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import com.allen_sauer.gwt.log.client.Log;
import com.mongodb.BasicDBObject;
import com.mongodb.DB;
import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
/**
* The Class AbstractStructuredData.
*
* @author alexript
*/
public abstract class AbstractStructuredData {
/** The tableprefix. */
private final String tableprefix;
/** The tablesuffix. */
private final String tablesuffix;
/** The tablename. */
private final String tablename;
/** The struct. */
protected Structure struct;
/** The values. */
protected StructValues values;
/** The Constant fieldname_domain. */
protected static final String fieldname_domain = "domain";
/** The Constant fieldname_username. */
protected static final String fieldname_username = "username";
/** The Constant fieldname_number. */
protected static final String fieldname_number = "number";
/** The Constant fieldname_createdate. */
protected static final String fieldname_createdate = "createdate";
/** The Constant fieldname_trash. */
protected static final String fieldname_trash = "trash";
/**
* Instantiates a new structured data.
*
* @param prefix
* the prefix
* @param suffix
* the suffix
* @param domain
* the domain
*/
public AbstractStructuredData(String prefix, String suffix, String domain) {
tableprefix = prefix;
tablesuffix = suffix;
tablename = prefix + "_" + suffix;
initMetaStructure(domain);
initDBStruct();
setTrash(false);
}
/**
* Adds the find all orders.
*
* @param o
* the o
*/
protected abstract void addFindAllOrders(final BasicDBObject o);
/**
* Adds the find all query parameters.
*
* @param q
* the q
*/
protected abstract void addFindAllQueryParameters(final BasicDBObject q);
/**
* Adds the find last number params.
*
* @param w
* the w
*/
protected abstract void addFindLastNumberParams(final BasicDBObject w);
/**
* Adds the get record params.
*
* @param query
* the query
*/
protected abstract void addGetRecordParams(final BasicDBObject query);
/**
* Creates the record.
*
* @return true, if successful
*/
private boolean createRecord() {
boolean result = false;
setNumber(findLastNumber());
setCreatedate(new Date());
onCreate();
DB db = Database.get(getDomain());
if (db != null) {
BasicDBObject doc = (BasicDBObject) store(null);
if (doc != null) {
Database.retain();
DBCollection coll = db.getCollection(getTableName());
coll.insert(doc);
Database.release();
result = true;
}
}
return result;
}
/**
* Dump.
*
* @return the string
*/
public String dump() {
StringBuilder sb = new StringBuilder();
sb.append("<" + getPrefix() + " name=\"" + getSuffix() + "\">\n");
sb.append(struct.toString());
sb.append("<records>\n");
Set<Long> numbers = findAll();
Iterator<Long> i = numbers.iterator();
while (i.hasNext()) {
Long number = i.next();
DBObject doc = getRecord(number);
if (doc != null) {
load(doc);
sb.append(values.toString());
}
}
sb.append("</records>\n");
StringBuilder child = onDump();
if (child != null) {
sb.append(child.toString());
}
sb.append("</" + getPrefix() + ">\n");
return sb.toString();
}
/**
* Find all.
*
* @return the sets the
*/
public Set<Long> findAll() {
Set<Long> numbers = new HashSet<Long>();
DB db = Database.get(getDomain());
if (db != null) {
Database.retain();
DBCollection coll = db.getCollection(getTableName());
BasicDBObject q = new BasicDBObject();
BasicDBObject w = new BasicDBObject();
q.put(fieldname_domain, getDomain());
q.put(fieldname_trash, false);
addFindAllQueryParameters(q);
w.put("$query", q);
BasicDBObject o = new BasicDBObject();
o.put(fieldname_number, 1);
addFindAllOrders(o);
w.put("$orderby", o);
DBCursor cur = coll.find(w);
while (cur.hasNext()) {
DBObject myDoc = cur.next();
numbers.add((Long) myDoc.get(fieldname_number));
}
Database.release();
}
return numbers;
}
/**
* Find last number.
*
* @return the long
*/
protected Long findLastNumber() {
Long last = 0L;
DB db = Database.get(getDomain());
if (db != null) {
Database.retain();
DBCollection coll = db.getCollection(getTableName());
BasicDBObject w = new BasicDBObject();
w.put(fieldname_domain, getDomain());
addFindLastNumberParams(w);
BasicDBObject query = new BasicDBObject();
query.put("$query", w);
query.put("$orderby", new BasicDBObject(fieldname_number, -1));
DBObject doc = null;
try {
doc = coll.findOne(query);
} catch (com.mongodb.MongoException e) {
last = 1L;
}
if (doc != null) {
last = (Long) doc.get(fieldname_number) + 1L;
}
Database.release();
}
if (last.equals(0L)) {
last = 1L;
}
return last;
}
/**
* From map.
*
* @param map
* the map
*/
public void fromMap(HashMap<String, Object> map) {
Set<String> names = struct.getNames();
Iterator<String> i = names.iterator();
while (i.hasNext()) {
String name = i.next();
if (!name.equals(fieldname_createdate)
&& !name.equals(fieldname_domain)
&& !name.equals(fieldname_number)
&& !name.equals(fieldname_username)) {
if (map.containsKey(name)) {
values.setObject(name, map.get(name));
}
}
}
}
/**
* Gets the.
*
* @param number
* the number
*/
protected void get(Long number) {
DBObject doc = getRecord(number);
if (doc != null) {
load(doc);
}
}
/**
* Gets the createdate.
*
* @return the createdate
*/
public Date getCreatedate() {
return (Date) values.get(fieldname_createdate);
}
/**
* Gets the domain.
*
* @return the domain
*/
public String getDomain() {
return (String) values.get(fieldname_domain);
}
/**
* Gets the field value.
*
* @param fieldname
* the fieldname
* @return the field value
*/
public Object getFieldValue(String fieldname) {
return values.get(fieldname);
}
/**
* Gets the number.
*
* @return the number
*/
public Long getNumber() {
return (Long) values.get(fieldname_number);
}
/**
* Gets the prefix.
*
* @return the prefix
*/
public String getPrefix() {
return tableprefix;
}
/**
* Gets the record.
*
* @param number
* the number
* @return the record
*/
private DBObject getRecord(Long number) {
DBObject doc = null;
DB db = Database.get(getDomain());
if (db != null) {
Database.retain();
DBCollection coll = db.getCollection(getTableName());
BasicDBObject query = new BasicDBObject();
query.put(fieldname_domain, getDomain());
addGetRecordParams(query);
query.put(fieldname_number, number);
doc = coll.findOne(query);
Database.release();
onGetRecord(number);
}
return doc;
}
/**
* Gets the suffix.
*
* @return the suffix
*/
public String getSuffix() {
return tablesuffix;
}
/**
* Gets the table name.
*
* @return the table name
*/
public String getTableName() {
return tablename;
}
/**
* Gets the username.
*
* @return the username
*/
public String getUsername() {
return (String) values.get(fieldname_username);
}
/**
* Inits the db struct.
*/
private void initDBStruct() {
DB db = Database.get(getDomain());
if (db != null) {
Database.retain();
DBCollection coll = db.getCollection(getTableName());
List<DBObject> indexes = coll.getIndexInfo();
if (indexes.size() < 1) {
BasicDBObject i = new BasicDBObject();
i.put(fieldname_number, 1);
coll.createIndex(i);
i.put(fieldname_domain, 1);
coll.createIndex(i);
i.put(fieldname_trash, 1);
coll.createIndex(i);
onInitDbStruct(i, coll);
}
Database.release();
}
}
/**
* Inits the global structure (in classes Catalog or Document or some other
* abstract class).
*/
protected abstract void initGlobalStructure();
/**
* Inits the document structure.
*/
private void initMetaStructure(String domain) {
struct = new Structure();
struct.add(fieldname_domain, DataTypes.DT_DOMAIN, "127.0.0.1");
struct.add(fieldname_username, DataTypes.DT_STRING, "uncknown");
struct.add(fieldname_number, DataTypes.DT_LONG, new Long(0L));
struct.add(fieldname_createdate, DataTypes.DT_DATE, new Date());
struct.add(fieldname_trash, DataTypes.DT_BOOLEAN, new Boolean(false));
initGlobalStructure();
initStructure(domain);
values = new StructValues(struct);
setDomain(domain);
}
/**
* Inits the structure for The class.
*/
protected abstract void initStructure(String domain);
/**
* Checks if is trash.
*
* @return true, if is trash
*/
public boolean isTrash() {
return (Boolean) values.get(fieldname_trash);
}
/**
* Load.
*
* @param doc
* the doc
*/
protected void load(DBObject doc) {
Set<String> names = struct.getNames();
Iterator<String> i = names.iterator();
while (i.hasNext()) {
String name = i.next();
values.set(name, doc.get(name));
}
}
/**
* On create.
*/
protected abstract void onCreate();
/**
* On dump.
*
* @return the string builder
*/
protected abstract StringBuilder onDump();
/**
* On get record.
*
* @param number
* the number
*/
protected abstract void onGetRecord(Long number);
/**
* On init db struct.
*
* @param i
* the i
* @param coll
* the coll
*/
protected abstract void onInitDbStruct(final BasicDBObject i,
final DBCollection coll);
/**
* On restore.
*
* @param dump
* the dump
*/
protected abstract void onRestore(Element dump);
/**
* On update.
*/
protected abstract void onUpdate();
/**
* Restore.
*/
public void restore() {
setTrash(false);
}
/**
* Restore.
*
* @param dump
* the dump
*/
public void restore(Element dump) {
DB db = Database.get(getDomain());
if (db != null) {
DBCollection coll = db.getCollection(getTableName());
Log.trace(getTableName());
NodeList recordsets = dump.getElementsByTagName("records");
for (int i = 0; i < recordsets.getLength(); i++) {
Element recordset = (Element) recordsets.item(i);
if (recordset.getParentNode() == dump) {
NodeList records = recordset.getElementsByTagName("record");
if (records.getLength() > 0) {
BasicDBObject q = new BasicDBObject();
q.put("domain", getDomain());
Database.retain();
coll.remove(q);
for (int j = 0; j < records.getLength(); j++) {
Log.trace("record");
BasicDBObject doc = new BasicDBObject();
Element record = (Element) records.item(j);
NodeList fields = record
.getElementsByTagName("field");
for (int k = 0; k < fields.getLength(); k++) {
Element field = (Element) fields.item(k);
String name = field.getAttribute("name");
if (name.equals("domain")) {
doc.put("domain", getDomain());
} else {
int type = struct.getType(name);
String sval = field.getAttribute("value");
Log.error("field name=" + name + " value="
+ sval);
doc.put(name,
DataTypes.fromString(type, sval));
}
}
Log.trace("insert");
coll.insert(doc);
}
}
}
}
Database.release();
onRestore(dump);
}
}
/**
* Save.
*
* @return true, if successful
*/
public boolean save() {
boolean result = false;
if (getNumber() < 1L) {
result = createRecord();
} else {
result = updateRecord();
}
return result;
}
/**
* Sets the createdate.
*
* @param createdate
* the createdate to set
*/
public void setCreatedate(Date createdate) {
values.set(fieldname_createdate, createdate);
}
/**
* Sets the domain.
*
* @param domain
* the domain to set
*/
public void setDomain(String domain) {
values.set(fieldname_domain, domain);
}
/**
* Sets the field value.
*
* @param fieldname
* the fieldname
* @param val
* the val
*/
public void setFieldValue(String fieldname, Object val) {
values.set(fieldname, val);
}
/**
* Sets the number.
*
* @param number
* the number to set
*/
public void setNumber(Long number) {
values.set(fieldname_number, number);
onGetRecord(number);
}
/**
* Sets the trash.
*
* @param flag
* the new trash
*/
public void setTrash(Boolean flag) {
values.set(fieldname_trash, flag);
}
/**
* Sets the username.
*
* @param username
* the username to set
*/
public void setUsername(String username) {
values.set(fieldname_username, username);
}
/**
* Store.
*
* @param doc
* the doc
* @return the dB object
*/
private DBObject store(DBObject doc) {
if (doc == null) {
doc = new BasicDBObject();
}
Set<String> names = struct.getNames();
Iterator<String> i = names.iterator();
while (i.hasNext()) {
String name = i.next();
doc.put(name, values.get(name));
}
doc.put(fieldname_trash, isTrash());
return doc;
}
/**
* To map.
*
* @return the hash map
*/
public HashMap<String, Object> toMap() {
HashMap<String, Object> map = new HashMap<String, Object>();
Set<String> names = struct.getNames();
Iterator<String> i = names.iterator();
while (i.hasNext()) {
String name = i.next();
map.put(name, values.getObject(name));
}
return map;
}
/**
* Trash.
*/
public void trash() {
setTrash(true);
}
/**
* Update record.
*
* @return true, if successful
*/
private boolean updateRecord() {
boolean result = false;
DBObject doc = getRecord(getNumber());
if (doc == null) {
result = createRecord();
} else {
onUpdate();
doc = store(doc);
DB db = Database.get(getDomain());
if (db != null) {
Database.retain();
DBCollection coll = db.getCollection(getTableName());
coll.save(doc);
Database.release();
result = true;
}
}
return result;
}
}