package nebula.data.impl;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import nebula.data.Classificator;
import nebula.data.Editable;
import nebula.data.Entity;
import nebula.data.SmartList;
import nebula.lang.Field;
import nebula.lang.Reference;
import nebula.lang.Type;
import nebula.lang.TypeStandalone;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.google.common.base.Function;
import com.google.common.collect.Maps;
public class EntityDataStore implements DataStoreEx<Entity> {
private static Log log = LogFactory.getLog(EntityDataStore.class);
final DataReposEx dataRepos;
final SmartList<Object, Entity> values;
final ReentrantLock lock = new ReentrantLock();
final Function<Entity, Object> idMaker;
long lastModified;
final Map<String, Classificator<String, Entity>> classificatores;
final Type type;
EntityDataStore(Function<Entity, Object> keyMaker, DataReposEx dataRepos, Type type) {
this.dataRepos = dataRepos;
this.values = new SmartList<Object, Entity>(keyMaker);
this.idMaker = keyMaker;
this.type = type;
classificatores = Maps.newHashMap();
for (Field f : type.getFields()) {
if (f.getAttrs().containsKey(Type.GROUP_BY) || f.getAttrs().containsKey(Type.ATTACH)) {
if (f.getType().getStandalone() == TypeStandalone.Basic) {
final String name = f.getName();
DataClassificator<String, Entity> classificator = new DataClassificator<String, Entity>( new Function<Entity, String>() {
@Override
public String apply(Entity from) {
return String.valueOf(from.get(name));
}
});
this.values.addListener(classificator);
classificatores.put(name, classificator);
if (log.isDebugEnabled()) {
log.debug(type.getName() + " add classificator " + name);
}
} else if (f.getRefer() == Reference.ByRef) {
for (Field inf : f.getType().getFields()) {
if (inf.isKey() && inf.getType().getStandalone() == TypeStandalone.Basic) {
final String name = f.getName() + inf.getName();
DataClassificator<String, Entity> classificator = new DataClassificator<String, Entity>( new Function<Entity, String>() {
@Override
public String apply(Entity from) {
return String.valueOf(from.get(name));
}
});
this.values.addListener(classificator);
classificatores.put(f.getName(), classificator);
if (log.isDebugEnabled()) {
log.debug(type.getName() + " add classificator " + name);
}
}
}
}
}
}
}
@Override
public Entity get(Object key) {
return this.values.get(key);
}
@Override
public void add(Entity v) {
EditableEntity entity = (EditableEntity) v;
if (v.isTransient()) {
entity.store = this;
}
dataRepos.markChanged(entity);
}
@Override
public void remove(Entity v) {
}
@Override
public void flush() {
dataRepos.flush();
}
@Override
public void markChanged(Editable v) {
dataRepos.markChanged(v);
}
@Override
public void apply(Entity newV) {
EditableEntity newEntity = (EditableEntity) newV;
if (newEntity.source != null) {
Map<String, Object> newData = new HashMap<String, Object>(newEntity.data);
newData.putAll(newEntity.newData);
lock.lock();
try {
EntityImp source = newEntity.source;
if (newEntity.data == source.data) {
source.data = newData;
newEntity.resetWith(source);
} else {
throw new RuntimeException("entity update by some others");
}
} finally {
lock.unlock();
}
} else {
Object id = idMaker.apply(newEntity);
newEntity.put(Entity.PRIMARY_KEY, id);
Map<String, Object> newData = new HashMap<String, Object>(newEntity.newData);
lock.lock();
try {
EntityImp source = new EntityImp(this, newData);
newEntity.resetWith(source);
this.values.add(source);
} finally {
lock.unlock();
}
}
}
@Override
public List<Entity> listAll() {
return this.values;
}
public void clearChanges() {
dataRepos.clearChanges();
}
@Override
public void load() {
}
@Override
public void unload() {
}
@Override
public long getLastModified() {
return lastModified;
}
@Override
public Classificator<String, Entity> getClassificator(String name) {
return this.classificatores.get(name);
}
@Override
public Map<String, Classificator<String, Entity>> getClassificatores() {
return this.classificatores;
}
@Override
public Type getType() {
return this.type;
}
}