package org.terasology.logic.manager; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.terasology.asset.*; import org.terasology.entitySystem.common.NullIterator; import org.terasology.rendering.assets.Texture; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.*; import java.util.logging.Level; import java.util.logging.Logger; public class AssetManager { private static AssetManager _instance = null; public static AssetManager getInstance() { if (_instance == null) { _instance = new AssetManager(); } return _instance; } private Logger logger = Logger.getLogger(this.getClass().getCanonicalName()); private Map<String, AssetSource> assetSources = Maps.newHashMap(); private EnumMap<AssetType, Map<String, AssetLoader>> assetLoaders = Maps.newEnumMap(AssetType.class); private Map<AssetUri, Asset> assetCache = Maps.newHashMap(); protected AssetManager() { } public void register(AssetType type, String extension, AssetLoader loader) { Map<String, AssetLoader> assetTypeMap = assetLoaders.get(type); if (assetTypeMap == null) { assetTypeMap = Maps.newHashMap(); assetLoaders.put(type, assetTypeMap); } assetTypeMap.put(extension.toLowerCase(Locale.ENGLISH), loader); } public void addAssetTemporary(AssetUri uri, Asset asset) { assetCache.put(uri, asset); } public Asset loadAsset(AssetUri uri) { if (!uri.isValid()) return null; Asset asset = assetCache.get(uri); if (asset != null) return asset; List<URL> urls = getAssetURLs(uri); if (urls.size() == 0) { logger.log(Level.WARNING, "Unable to resolve asset: " + uri); return null; } for (URL url : urls) { int extensionIndex = url.toString().lastIndexOf('.'); if (extensionIndex == -1) continue; String extension = url.toString().substring(extensionIndex + 1).toLowerCase(Locale.ENGLISH); Map<String, AssetLoader> extensionMap = assetLoaders.get(uri.getAssetType()); if (extensionMap == null) continue; AssetLoader loader = extensionMap.get(extension); if (loader == null) continue; InputStream stream = null; try { stream = url.openStream(); urls.remove(url); urls.add(0, url); asset = loader.load(stream, uri, urls); if (asset != null) { assetCache.put(uri, asset); } logger.log(Level.INFO, "Loaded " + uri); return asset; } catch (IOException ioe) { logger.log(Level.SEVERE, "Error reading asset " + uri, ioe); return null; } finally { if (stream != null) { try { stream.close(); } catch (IOException innerException) { logger.log(Level.SEVERE, "Error closing stream for " + uri, innerException); } } } } logger.log(Level.WARNING, "Unable to resolve asset: " + uri); return null; } public void clear() { // TODO: Unload assets //assetCache.clear(); } public void addAssetSource(AssetSource source) { assetSources.put(source.getSourceId().toLowerCase(Locale.ENGLISH), source); } public void removeAssetSource(AssetSource source) { assetSources.remove(source.getSourceId().toLowerCase(Locale.ENGLISH)); } public Iterable<AssetUri> listAssets() { return new Iterable<AssetUri>() { @Override public Iterator<AssetUri> iterator() { return new AllAssetIterator(); } }; } public Iterable<AssetUri> listAssets(final AssetType type) { return new Iterable<AssetUri>() { @Override public Iterator<AssetUri> iterator() { return new TypedAssetIterator(type); } }; } public List<URL> getAssetURLs(AssetUri uri) { AssetSource source = assetSources.get(uri.getPackage()); if (source != null) { return source.get(uri); } return Lists.newArrayList(); } public InputStream getAssetStream(AssetUri uri) throws IOException { List<URL> assetURLs = getAssetURLs(uri); if (assetURLs.isEmpty()) { return null; } return assetURLs.get(0).openStream(); } // Static syntax sugar public static InputStream assetStream(AssetUri uri) throws IOException { return getInstance().getAssetStream(uri); } public static Iterable<AssetUri> list() { return getInstance().listAssets(); } public static Iterable<AssetUri> list(AssetType type) { return getInstance().listAssets(type); } public static Asset load(AssetUri uri) { return getInstance().loadAsset(uri); } public static <T extends Asset> T load(AssetUri uri, Class<T> assetClass) { Asset result = load(uri); if (result != null && assetClass.isAssignableFrom(result.getClass())) { return assetClass.cast(result); } return null; } // Some ease-of-use helper methods public static Texture loadTexture(String simpleUri) { return load(new AssetUri(AssetType.TEXTURE, simpleUri), Texture.class); } private class AllAssetIterator implements Iterator<AssetUri> { Iterator<AssetSource> sourceIterator; Iterator<AssetUri> currentUriIterator; AssetUri next = null; public AllAssetIterator() { sourceIterator = assetSources.values().iterator(); if (sourceIterator.hasNext()) { currentUriIterator = sourceIterator.next().list().iterator(); } else { currentUriIterator = NullIterator.newInstance(); } iterate(); } @Override public boolean hasNext() { return next != null; } @Override public AssetUri next() { AssetUri result = next; iterate(); return result; } @Override public void remove() { throw new UnsupportedOperationException(); } private void iterate() { while (!currentUriIterator.hasNext() && sourceIterator.hasNext()) { currentUriIterator = sourceIterator.next().list().iterator(); } if (currentUriIterator.hasNext()) { next = currentUriIterator.next(); } else { next = null; } } } private class TypedAssetIterator implements Iterator<AssetUri> { AssetType type; Iterator<AssetSource> sourceIterator; Iterator<AssetUri> currentUriIterator; AssetUri next = null; public TypedAssetIterator(AssetType type) { this.type = type; sourceIterator = assetSources.values().iterator(); if (sourceIterator.hasNext()) { currentUriIterator = sourceIterator.next().list(type).iterator(); } else { currentUriIterator = NullIterator.newInstance(); } iterate(); } @Override public boolean hasNext() { return next != null; } @Override public AssetUri next() { AssetUri result = next; iterate(); return result; } @Override public void remove() { throw new UnsupportedOperationException(); } private void iterate() { while (!currentUriIterator.hasNext() && sourceIterator.hasNext()) { currentUriIterator = sourceIterator.next().list(type).iterator(); } if (currentUriIterator.hasNext()) { next = currentUriIterator.next(); } else { next = null; } } } }