/**
* eAdventure (formerly <e-Adventure> and <e-Game>) is a research project of the
* <e-UCM> research group.
*
* Copyright 2005-2010 <e-UCM> research group.
*
* You can access a list of all the contributors to eAdventure at:
* http://e-adventure.e-ucm.es/contributors
*
* <e-UCM> is a research group of the Department of Software Engineering
* and Artificial Intelligence at the Complutense University of Madrid
* (School of Computer Science).
*
* C Profesor Jose Garcia Santesmases sn,
* 28040 Madrid (Madrid), Spain.
*
* For more info please visit: <http://e-adventure.e-ucm.es> or
* <http://www.e-ucm.es>
*
* ****************************************************************************
*
* This file is part of eAdventure, version 2.0
*
* eAdventure 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 3 of the License, or
* (at your option) any later version.
*
* eAdventure 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 eAdventure. If not, see <http://www.gnu.org/licenses/>.
*/
package es.eucm.ead.engine.factories;
import com.badlogic.gdx.utils.Pool;
import com.google.inject.Singleton;
import es.eucm.ead.engine.gameobjects.GameObject;
import es.eucm.ead.model.elements.BasicElement;
import es.eucm.ead.model.elements.extra.EAdMap;
import es.eucm.ead.tools.GenericInjector;
import es.eucm.ead.tools.IdGenerator;
import es.eucm.ead.tools.reflection.ReflectionProvider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.HashMap;
import java.util.Map;
/**
* <p>
* Interface for a game object factory.
* </p>
* <p>
* This interface has the methods to provide a game object based on an element
* in the eAdventure model and a method to provide an instance of a game object
* of a given class.
* </p>
*/
@Singleton
public class GameObjectFactory<S extends BasicElement, T extends GameObject<?>> {
private static final Logger logger = LoggerFactory
.getLogger("GameObjectFactoryImpl");
private ReflectionProvider reflectionProvider;
private GenericInjector injector;
private IdGenerator idGenerator;
private boolean useCache;
private String idPrefix;
protected Map<String, T> cache;
protected Map<Class<?>, Pool<T>> pools;
private Map<Class<? extends S>, Class<? extends T>> classMap;
public GameObjectFactory(String idPrefix, boolean useCache,
ReflectionProvider reflectionProvider, GenericInjector injector) {
this.useCache = useCache;
this.idPrefix = idPrefix;
this.idGenerator = new IdGenerator();
if (useCache) {
cache = new HashMap<String, T>();
}
this.reflectionProvider = reflectionProvider;
this.injector = injector;
// Pools
this.pools = new HashMap<Class<?>, Pool<T>>();
}
public void setClassMap(Map<Class<? extends S>, Class<? extends T>> classMap) {
this.classMap = classMap;
}
@SuppressWarnings( { "unchecked", "rawtypes" })
/**
* Returns a {@link GameObject} in the engine's runtime model for a
* {@link EAdElement} in the eAdventure game model
*
* @param <T>
* The type of the {@link EAdElement}
* @param element
* The element
* @return The game object of that element
*/
public T get(S element) {
if (element == null)
return null;
GameObject temp = null;
if (useCache && element.getId() != null) {
temp = cache.get(element.getId());
if (temp != null)
return (T) temp;
}
Class<?> elementClass = element.getClass();
Class<?> runtimeClass = classMap.get(elementClass);
while (elementClass != null && runtimeClass == null) {
runtimeClass = classMap.get(elementClass);
elementClass = reflectionProvider.getSuperclass(elementClass);
}
if (runtimeClass == null) {
logger.error("No game element mapped for class {}", element
.getClass());
} else {
temp = (GameObject) getInstance(runtimeClass);
if (temp == null) {
logger.error("No instance for game object of class {}", element
.getClass());
} else {
if (element.getId() == null) {
element.setId(idGenerator.generateNewId(idPrefix));
}
temp.setElement(element);
if (useCache) {
cache.put(element.getId(), (T) temp);
}
}
}
return (T) temp;
}
public boolean remove(S element) {
return cache == null || cache.remove(element.getId()) != null;
}
@SuppressWarnings("unchecked")
public void remove(T gameObject) {
if (remove((S) gameObject.getElement())) {
Pool<T> pool = pools.get(gameObject.getClass());
if (pool != null) {
pool.free(gameObject);
gameObject.release();
}
}
}
public void clean() {
pools.clear();
if (cache != null)
cache.clear();
}
/**
* Adds a new relation between some model class and its engine
* interpretation
*
* @param clazz1
* model class
* @param clazz2
* game object class
*/
public void put(Class<? extends S> clazz1, Class<? extends T> clazz2) {
classMap.put(clazz1, clazz2);
}
@SuppressWarnings("unchecked")
public void put(EAdMap<String> binds) {
if (binds != null) {
for (Map.Entry<String, String> e : binds.entrySet()) {
Class c1 = reflectionProvider.getClassforName(e.getKey());
Class c2 = reflectionProvider.getClassforName(e.getValue());
put(c1, c2);
}
}
}
private T getInstance(Class<?> clazz) {
Pool<T> pool = pools.get(clazz);
if (pool == null) {
pool = new GameObjectPool(clazz);
pools.put(clazz, pool);
}
return pool.obtain();
}
public class GameObjectPool extends Pool<T> {
private Class<?> clazz;
public GameObjectPool(Class<?> clazz) {
this.clazz = clazz;
}
@SuppressWarnings("unchecked")
@Override
protected T newObject() {
return (T) injector.getInstance(clazz);
}
}
/**
* Returns how many elements are contained by the cache of this factory
*
* @return
*/
public int getCacheSize() {
return cache != null ? cache.size() : 0;
}
}