/** * 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.assets; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.files.FileHandle; import com.google.inject.Inject; import com.google.inject.Singleton; import es.eucm.ead.engine.assets.drawables.RuntimeDrawable; import es.eucm.ead.engine.assets.fonts.RuntimeFont; import es.eucm.ead.engine.factories.mapproviders.AssetHandlerMap; import es.eucm.ead.model.assets.AssetDescriptor; import es.eucm.ead.model.assets.drawable.EAdDrawable; import es.eucm.ead.model.assets.text.EAdFont; import es.eucm.ead.model.elements.scenes.Scene; import es.eucm.ead.model.interfaces.features.Resourced; import es.eucm.ead.tools.GenericInjector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * <p> * Implementation of the basic methods in the asset handler. * </p> * <p> * This abstract implementation has a map that works as a cache of runtime * assets (this do not have to be loaded, so they can use very little memory.<br> * A class map, use to know how to load an asset of different types based on the * descriptor is inyected to this class. * </p> */ @Singleton public abstract class AssetHandlerImpl implements AssetHandler { public static final String ENGINE_RESOURCES_PATH = "es/eucm/ead/engine/resources/"; public static final String PROJECT_INTERNAL_PATH = ""; protected GenericInjector injector; /** * The class logger */ protected static Logger logger = LoggerFactory .getLogger(AssetHandlerImpl.class); /** * A cache of the runtime assets for each asset descriptor */ final private Map<AssetDescriptor, RuntimeAsset<?>> cache; /** * Cache for special assets renderers */ protected Map<AssetDescriptor, SpecialAssetRenderer<?, ?>> specialRenderers; /** * A class map of the asset descriptor classes and their corresponding * runtime assets */ private Map<Class<? extends AssetDescriptor>, Class<? extends RuntimeAsset<? extends AssetDescriptor>>> classMap; protected String resourcesUri; private boolean cacheEnabled; private ArrayList<AssetDescriptor> assetsQueue; protected String currentLanguage; @Inject public AssetHandlerImpl(GenericInjector injector) { this.injector = injector; this.classMap = new AssetHandlerMap().getMap(); cache = new HashMap<AssetDescriptor, RuntimeAsset<?>>(); specialRenderers = new HashMap<AssetDescriptor, SpecialAssetRenderer<?, ?>>(); cacheEnabled = true; assetsQueue = new ArrayList<AssetDescriptor>(); } public void queueSceneToLoad(Scene scene) { // FIXME /*List<AssetDescriptor> list = sceneGraph.getSceneAssets().get(scene); if (list == null) { logger.warn("Assets for scene {} were empty in the scene graph", scene.getId()); } else { int i = 0; for (AssetDescriptor a : list) { if (!cache.containsKey(a)) { assetsQueue.add(a); i++; } } if (i > 0) { logger.info("{} assets will be loaded", i); } }*/ } public void clearAssetQueue() { assetsQueue.clear(); } public boolean loadStep() { if (assetsQueue.size() > 0) { AssetDescriptor asset = assetsQueue.remove(0); getRuntimeAsset(asset, true); } return assetsQueue.size() > 0; } /* * (non-Javadoc) * * @see * es.eucm.eadventure.engine.engine.platform.AssetHandler#getRuntimeAsset( * es.eucm.eadventure.common.resources.assets.AssetDescriptor) */ @SuppressWarnings("all") @Override public <T extends AssetDescriptor> RuntimeAsset<T> getRuntimeAsset( T descriptor) { if (descriptor == null) { return null; } RuntimeAsset<T> temp = null; if (cacheEnabled) { synchronized (cache) { temp = (RuntimeAsset<T>) cache.get(descriptor); } } if (temp == null) { Class<?> clazz = descriptor.getClass(); Class<? extends RuntimeAsset<?>> tempClass = null; while (clazz != null && tempClass == null) { tempClass = classMap.get(clazz); clazz = clazz.getSuperclass(); } temp = (RuntimeAsset<T>) getInstance(tempClass); temp.setDescriptor(descriptor); if (cacheEnabled) { synchronized (cache) { cache.put(descriptor, temp); } } } return temp; } @Override public <T extends AssetDescriptor> RuntimeAsset<T> getRuntimeAsset( T descriptor, boolean load) { RuntimeAsset<T> runtimeAsset = getRuntimeAsset(descriptor); if (runtimeAsset == null) { logger.warn("No runtime asset for {}", descriptor); } else if (load && !runtimeAsset.isLoaded()) { runtimeAsset.loadAsset(); } return runtimeAsset; } @Override public <T extends EAdDrawable> RuntimeDrawable<T> getDrawableAsset( T descriptor) { return (RuntimeDrawable<T>) getRuntimeAsset(descriptor); } /* * (non-Javadoc) * * @see * es.eucm.eadventure.engine.engine.platform.AssetHandler#getRuntimeAsset( * es.eucm.eadventure.common.model.EAdElement, * es.eucm.eadventure.common.resources.EAdBundleId, java.lang.String) */ @Override public RuntimeAsset<?> getRuntimeAsset(Resourced element, String bundleId, String id) { AssetDescriptor descriptor = element.getAsset(bundleId, id); if (descriptor == null) { descriptor = element.getAsset(id); } if (descriptor == null) { logger.error("No such asset. element: " + element.getClass() + "; bundleId: " + (bundleId != null ? bundleId : "null") + "; id: " + id); return null; } return getRuntimeAsset(descriptor); } @Override public RuntimeAsset<?> getRuntimeAsset(Resourced element, String id) { return getRuntimeAsset(element, null, id); } @Override public boolean isLoaded() { return Gdx.files != null; } private ArrayList<AssetDescriptor> descriptorsToRemove = new ArrayList<AssetDescriptor>(); @Override public void clean(List<AssetDescriptor> exceptions) { descriptorsToRemove.clear(); if (exceptions != null) { for (AssetDescriptor asset : cache.keySet()) { if (!exceptions.contains(asset)) { descriptorsToRemove.add(asset); } } } else { descriptorsToRemove.addAll(cache.keySet()); } for (AssetDescriptor a : descriptorsToRemove) { RuntimeAsset<?> asset = cache.remove(a); if (asset != null) { asset.freeMemory(); } } logger.info(descriptorsToRemove.size() + " unused assets were remove from the cache"); } public void clean() { for (RuntimeAsset<?> a : cache.values()) { a.freeMemory(); } cache.clear(); } @Override public void setResourcesLocation(String uri) { if (!uri.endsWith("/")) { uri += "/"; } this.resourcesUri = uri; } @Override public void setCacheEnabled(boolean enable) { this.cacheEnabled = enable; } public void setLanguage(String currentLanguage) { if (this.currentLanguage == null || !this.currentLanguage.equals(currentLanguage)) { this.currentLanguage = currentLanguage; refresh(); } } @Override public void refresh() { for (RuntimeAsset<?> r : cache.values()) { r.refresh(); } } public void remove(AssetDescriptor assetDescriptor) { RuntimeAsset<?> asset; synchronized (cache) { asset = cache.remove(assetDescriptor); } if (asset != null && asset.isLoaded()) { asset.freeMemory(); } } @Override public void terminate() { } @Override public String getTextFile(String path) { FileHandle fh = getFileHandleLocalized(path.substring(1)); if (fh != null) { String text = ""; BufferedReader reader = null; try { reader = new BufferedReader(fh.reader()); String line; while ((line = reader.readLine()) != null) { text += (line + "\n"); } } catch (Exception e) { logger.error("Error reading text file {}", path, e); } finally { try { if (reader != null) { reader.close(); } } catch (IOException e) { logger.error("Error reading text file {}", e); } } return text; } return null; } public RuntimeAsset<?> getInstance(Class<? extends RuntimeAsset<?>> clazz) { return injector.getInstance(clazz); } public FileHandle getFileHandle(String path) { String uri = path.substring(1); FileHandle fh = null; if (currentLanguage != null && !"".equals(currentLanguage)) { fh = getFileHandleLocalized(currentLanguage + "/" + uri); } if (fh == null || !fh.exists()) { fh = getFileHandleLocalized(uri); } return fh; } /** * retrieves a file handle for the path * * @param uri the uri * @return the file handle */ public FileHandle getFileHandleLocalized(String uri) { if (resourcesUri != null) { FileHandle absolute = getProjectFileHandle(uri); if (absolute.exists()) { return absolute; } } FileHandle internal = getProjectInternal(uri); if (internal.exists()) { return internal; } return getEngineFileHandle(uri); } public FileHandle getProjectFileHandle(String uri) { FileHandle fh = Gdx.files.absolute(resourcesUri + "/" + uri); if (!fh.exists()) { fh = Gdx.files.internal(resourcesUri + "/" + uri); } return fh; } public FileHandle getProjectInternal(String uri) { return Gdx.files.internal(PROJECT_INTERNAL_PATH + uri); } public FileHandle getEngineFileHandle(String uri) { return Gdx.files.internal(ENGINE_RESOURCES_PATH + uri); } public int getCacheSize() { return cache != null ? cache.size() : 0; } public boolean fileExists(String path) { FileHandle fh = getFileHandle(path); return (fh != null && fh.exists()); } public String read(String file) { return getTextFile(file); } public RuntimeFont getFont(EAdFont font) { return (RuntimeFont) getRuntimeAsset(font); } }