Skip to content

Commit

Permalink
Add index for entities in store
Browse files Browse the repository at this point in the history
  • Loading branch information
danschultz committed Aug 9, 2011
1 parent 6063f45 commit e3432d2
Show file tree
Hide file tree
Showing 4 changed files with 242 additions and 8 deletions.
115 changes: 109 additions & 6 deletions src/mesh/model/Store.as
Expand Up @@ -7,21 +7,28 @@ package mesh.model

import mesh.core.array.flatten;
import mesh.core.reflection.newInstance;
import mesh.model.query.Queries;
import mesh.model.query.Query;
import mesh.source.Source;

import mx.events.PropertyChangeEvent;

public class Store extends EventDispatcher
{
private var _keyCounter:Number = 0;
private var _keyToEntity:Dictionary = new Dictionary();
private var _index:EntityIndex;
private var _changes:HashSet = new HashSet();

private var _queries:Queries;

private var _dataSource:Source;

public function Store(dataSource:Source)
{
super();

_index = new EntityIndex(this);
_queries = new Queries(this);
_dataSource = dataSource;
}

Expand Down Expand Up @@ -61,9 +68,38 @@ package mesh.model
}
}

public function find(...args):void
/**
* Finds a single entity for a specific ID, or returns a list of entities
* matching a query.
*
* <p>
* If you supply an entity type and an ID, then this method returns a single
* <code>Entity</code>. If the entity has not been loaded into the store, then
* the store will ask the data source to load it. All properties on the entity
* will be empty until the data source has loaded the data.
* </p>
*
* <p>
* If you supply a query, then this method returns a <code>ResultList</code>
* of all entities that match the conditions of the query.
* </p>
*
* @param args A entity type and an ID, or a <code>Query</code>.
* @return An <code>Entity</code> or <code>ResultList</code>.
*/
public function find(...args):*
{
// A single entity is being requested.
if (args.length == 2 && args[0] is Class) {

}

// A result list is being requested.
if (args[0] is Query) {
return _queries.result(args[0]);
}

throw new ArgumentError("Invalid find arguments: " + args);
}

/**
Expand All @@ -90,21 +126,88 @@ package mesh.model

private function register(entity:Entity):void
{
_keyToEntity[entity.storeKey] = entity;
entity.store = this;
entity.storeKey = generateStoreKey();
entity.store = this;
entity.addEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handleEntityPropertyChange);
_index.add(entity);
}

private function unregister(entity:Entity):void
{
if (_keyToEntity[entity.storeKey] == null) {
if (!_index.contains(entity)) {
throw new ArgumentError("Entity '" + entity + "' not found in store");
}
entity.store = null;
entity.removeEventListener(PropertyChangeEvent.PROPERTY_CHANGE, handleEntityPropertyChange);
delete _keyToEntity[entity.storeKey];
_index.remove(entity);
_changes.remove(entity);
}
}
}

import collections.HashSet;

import flash.utils.Dictionary;

import mesh.model.Entity;

/**
* An index for the entities in a store.
*
* @author Dan Schultz
*/
class EntityIndex extends HashSet
{
private var _keyToEntity:Dictionary = new Dictionary();

/**
* Constructor.
*/
public function EntityIndex()
{

}

/**
* @inheritDoc
*/
override public function add(entity:Entity):Boolean
{
if (super.add(entity)) {
_keyToEntity[entity.storeKey] = entity;
return true;
}
return false;
}

/**
* @inheritDoc
*/
override public function contains(entity:Entity):Boolean
{
return forKey(entity.storeKey) != null;
}

/**
* Returns an <code>Entity</code> that has the given store key.
*
* @param key The store key mapped to an entity.
* @return An entity with the given store key.
*/
public function forKey(key:Object):Entity
{
return _keyToEntity[key];
}

/**
* @inheritDoc
*/
override public function remove(entity:Entity):Boolean
{
if (super.remove(entity)) {
delete _keyToEntity[entity.storeKey];
return true;
}
return false;
}
}
47 changes: 47 additions & 0 deletions src/mesh/model/query/Queries.as
@@ -0,0 +1,47 @@
package mesh.model.query
{
import collections.HashMap;

import mesh.model.Store;

/**
* A class that caches the results of each query in the store.
*
* @author Dan Schultz
*/
public class Queries
{
private var _store:Store;
private var _cache:HashMap = new HashMap();

/**
* Constructor.
*
* @param store The store that owns these queries.
*/
public function Queries(store:Store)
{
_store = store;
}

/**
* Returns the cached result for the given query. If the result hasn't been
* cached, then a new result is created.
*
* @param query The query to get the results for.
* @return The query's result.
*/
public function result(query:Query):ResultList
{
if (!isCached(query)) {
_cache.put(query, new ResultList(query, _store).refresh());
}
return _cache.grab(query);
}

private function isCached(query:Query):Boolean
{
return _cache.containsKey(query);
}
}
}
36 changes: 34 additions & 2 deletions src/mesh/model/query/Query.as
@@ -1,5 +1,6 @@
package mesh.model.query
{
import mesh.core.reflection.reflect;
import mesh.model.Entity;

/**
Expand All @@ -13,9 +14,9 @@ package mesh.model.query
/**
* Constructor.
*/
public function Query()
public function Query(term:String)
{

_term = term;
}

/**
Expand Down Expand Up @@ -43,5 +44,36 @@ package mesh.model.query
{
return false;
}

/**
* Checks if this query is the same as another query.
*
* @param query The query to check.
* @return <code>true</code> if the queries are the same.
*/
public function equals(query:Object):Boolean
{
return reflect(this).clazz == reflect(query).clazz &&
term == Query( query ).term;
}

/**
* Returns the hash for this query, which is the query's term.
*
* @return A hash.
*/
public function hashCode():Object
{
return term;
}

private var _term:String;
/**
* The term defining the query.
*/
public function get term():String
{
return _term;
}
}
}
52 changes: 52 additions & 0 deletions src/mesh/model/query/ResultList.as
@@ -0,0 +1,52 @@
package mesh.model.query
{
import mesh.core.List;
import mesh.model.Store;

/**
* The <code>ResultList</code> is a list of entities after an execution of a
* query.
*
* @author Dan Schultz
*/
public class ResultList extends List
{
private var _query:Query;
private var _store:Store;

/**
* Constructor.
*
* @param query The query bound to this result.
* @param store The store that the query is executing against.
*/
public function ResultList(query:Query, store:Store)
{
super();
_query = query;
_store = store;
}

/**
* Refreshes the results of this list based on the list's query.
*
* @return This list.
*/
public function refresh():ResultList
{
return this;
}

/**
* @inheritDoc
*/
override public function get source():Array
{
return super.source.concat();
}
override public function set source(value:Array):void
{
// Don't allow clients to set the source.
}
}
}

0 comments on commit e3432d2

Please sign in to comment.