AssetManager: javadoc, cleanup, new method

* Move *cache* methods up into the AssetManager interface from DesktopAssetManager
 * Fix various incorrect javadoc
 * Add new method to load an asset from an InputStream
 * Restructure DesktopAssetManager.loadAsset() to make it less monolithic
experimental
shadowislord 10 years ago
parent f7624be761
commit 90925e3968
  1. 162
      jme3-core/src/main/java/com/jme3/asset/AssetManager.java
  2. 169
      jme3-core/src/main/java/com/jme3/asset/DesktopAssetManager.java
  3. 60
      jme3-core/src/main/java/com/jme3/asset/StreamAssetInfo.java

@ -46,6 +46,8 @@ import com.jme3.shader.ShaderGenerator;
import com.jme3.shader.ShaderKey;
import com.jme3.texture.Texture;
import com.jme3.texture.plugins.TGALoader;
import java.io.IOException;
import java.io.InputStream;
import java.util.EnumSet;
import java.util.List;
@ -75,7 +77,15 @@ import java.util.List;
* <li>{@link TGALoader} - Used to load Targa image files</li>
* </ul>
* <p>
* Once the asset has been loaded,
* Once the asset has been loaded, it will be
* {@link AssetProcessor#postProcess(com.jme3.asset.AssetKey, java.lang.Object)
* post-processed} by the {@link AssetKey#getProcessorType() key's processor}.
* If the key specifies a {@link AssetKey#getCacheType() cache type}, the asset
* will be cached in the specified cache. Next, the {@link AssetProcessor}
* will be requested to {@link AssetProcessor#createClone(java.lang.Object) }
* generate a clone for the asset. Some assets do not require cloning,
* such as immutable or shared assets. Others, like models, must be cloned
* so that modifications to one instance do not leak onto others.
*/
public interface AssetManager {
@ -100,46 +110,13 @@ public interface AssetManager {
*/
public List<ClassLoader> getClassLoaders();
/**
* Registers a loader for the given extensions.
*
* @param loaderClassName
* @param extensions
*
* @deprecated Please use {@link #registerLoader(java.lang.Class, java.lang.String[]) }
* together with {@link Class#forName(java.lang.String) } to find a class
* and then register it.
*
* @deprecated Please use {@link #registerLoader(java.lang.Class, java.lang.String[]) }
* with {@link Class#forName(java.lang.String) } instead.
*/
@Deprecated
public void registerLoader(String loaderClassName, String ... extensions);
/**
* Registers an {@link AssetLocator} by using a class name.
* See the {@link AssetManager#registerLocator(java.lang.String, java.lang.Class) }
* method for more information.
*
* @param rootPath The root path from which to locate assets, this
* depends on the implementation of the asset locator.
* A URL based locator will expect a url folder such as "http://www.example.com/"
* while a File based locator will expect a file path (OS dependent).
* @param locatorClassName The full class name of the {@link AssetLocator}
* implementation.
*
* @deprecated Please use {@link #registerLocator(java.lang.String, java.lang.Class) }
* together with {@link Class#forName(java.lang.String) } to find a class
* and then register it.
*/
@Deprecated
public void registerLocator(String rootPath, String locatorClassName);
/**
* Register an {@link AssetLoader} by using a class object.
*
* @param loaderClass
* @param extensions
* @param loaderClass The loader class to register.
* @param extensions Which extensions this loader is responsible for loading,
* if there are already other loaders registered for that extension, they
* will be overridden - there should only be one loader for each extension.
*/
public void registerLoader(Class<? extends AssetLoader> loaderClass, String ... extensions);
@ -161,7 +138,7 @@ public interface AssetManager {
* to the {@link AssetLoader} to load the asset.
* Once a locator is registered, it can be removed via
* {@link #unregisterLocator(java.lang.String, java.lang.Class) }.
*
*
* @param rootPath Specifies the root path from which to locate assets
* for the given {@link AssetLocator}. The purpose of this parameter
* depends on the type of the {@link AssetLocator}.
@ -206,18 +183,6 @@ public interface AssetManager {
*/
public void clearAssetEventListeners();
/**
* Set an {@link AssetEventListener} to receive events from this
* <code>AssetManager</code>. Any currently added listeners are
* cleared and then the given listener is added.
*
* @param listener The listener to set
* @deprecated Please use {@link #addAssetEventListener(com.jme3.asset.AssetEventListener) }
* to listen for asset events.
*/
@Deprecated
public void setAssetEventListener(AssetEventListener listener);
/**
* Manually locates an asset with the given {@link AssetKey}.
* This method should be used for debugging or internal uses.
@ -233,6 +198,23 @@ public interface AssetManager {
*/
public AssetInfo locateAsset(AssetKey<?> key);
/**
* Load an asset from an {@link InputStream}.
* In some cases it may be required to load an asset from memory
* or arbitrary streams so that registering a custom locator and key
* type is not necessary.
*
* @param <T> The object type that will be loaded from the AssetKey instance.
* @param key The AssetKey. Note that the asset will not be cached -
* following the same behavior as if {@link AssetKey#getCacheType()} returned null.
* @param inputStream The input stream from which the asset shall be loaded.
* @return The loaded asset.
*
* @throws AssetLoadException If the {@link AssetLoader} has failed
* to load the asset due to an {@link IOException} or another error.
*/
public <T> T loadAssetFromStream(AssetKey<T> key, InputStream inputStream);
/**
* Load an asset from a key, the asset will be located
* by one of the {@link AssetLocator} implementations provided in the
@ -244,17 +226,18 @@ public interface AssetManager {
*
* @param <T> The object type that will be loaded from the AssetKey instance.
* @param key The AssetKey
* @return The loaded asset, or null if it was failed to be located
* or loaded.
* @return The loaded asset.
*
* @throws AssetNotFoundException If all registered locators have failed
* to locate the asset.
* @throws AssetLoadException If the {@link AssetLoader} has failed
* to load the asset due to an {@link IOException} or another error.
*/
public <T> T loadAsset(AssetKey<T> key);
/**
* Load an asset by name, calling this method
* is the same as calling
* <code>
* loadAsset(new AssetKey(name)).
* </code>
* Load an asset by name, calling this method is the same as calling
* <code>loadAsset(new AssetKey(name))</code>.
*
* @param name The name of the asset to load.
* @return The loaded asset, or null if failed to be loaded.
@ -265,7 +248,7 @@ public interface AssetManager {
/**
* Loads texture file, supported types are BMP, JPG, PNG, GIF,
* TGA and DDS.
* TGA, DDS, PFM, and HDR.
*
* @param key The {@link TextureKey} to use for loading.
* @return The loaded texture, or null if failed to be loaded.
@ -276,8 +259,10 @@ public interface AssetManager {
/**
* Loads texture file, supported types are BMP, JPG, PNG, GIF,
* TGA and DDS.
* TGA, DDS, PFM, and HDR.
*
* The texture will be loaded with mip-mapping enabled.
*
* @param name The name of the texture to load.
* @return The texture that was loaded
*
@ -306,7 +291,8 @@ public interface AssetManager {
/**
* Loads a 3D model with a ModelKey.
* Models can be jME3 object files (J3O) or OgreXML/OBJ files.
* Models can be jME3 object files (J3O), OgreXML (mesh.xml), BLEND, FBX
* and OBJ files.
* @param key Asset key of the model to load
* @return The model that was loaded
*
@ -315,8 +301,9 @@ public interface AssetManager {
public Spatial loadModel(ModelKey key);
/**
* Loads a 3D model. Models can be jME3 object files (J3O) or
* OgreXML/OBJ files.
* Loads a 3D model. Models can be jME3 object files (J3O),
* OgreXML (mesh.xml), BLEND, FBX and OBJ files.
*
* @param name Asset name of the model to load
* @return The model that was loaded
*
@ -381,4 +368,53 @@ public interface AssetManager {
*/
public ShaderGenerator getShaderGenerator(EnumSet<Caps> caps);
/**
* Retrieve an asset from the asset cache.
*
* <b>NOTE:</b> Do <em>not</em> modify the returned asset!
* It is the same reference as what is stored in the cache, therefore any
* modifications to it will leak onto assets loaded from the same key in the future.
*
* @param <T> The object type that will be retrieved from the AssetKey instance.
* @param key The AssetKey to get from the cache.
* @return The cached asset, if found. Otherwise, <code>null</code>.
*
* @throws IllegalArgumentException If {@link AssetKey#getCacheType() caching}
* is disabled for the key.
*/
public <T> T getFromCache(AssetKey<T> key);
/**
* Inject an asset into the asset cache.
*
* <b>NOTE:</b> Do <em>not</em> modify the cached asset after storing!
* It is the same reference as what is stored in the cache, therefore any
* modifications to it will leak onto assets loaded from the same key in the future.
*
* @param <T> The object type of the asset.
* @param key The key where the asset shall be stored.
* @param asset The asset to inject into the cache.
*
* @throws IllegalArgumentException If {@link AssetKey#getCacheType() caching}
* is disabled for the key.
*/
public <T> void addToCache(AssetKey<T> key, T asset);
/**
* Delete an asset from the asset cache.
*
* @param <T> The object type of the AssetKey instance.
* @param key The asset key to remove from the cache.
* @return True if the asset key was found in the cache and was removed
* successfully. False if the asset key was not present in the cache.
*
* @throws IllegalArgumentException If {@link AssetKey#getCacheType() caching}
* is disabled for the key.
*/
public <T> boolean deleteFromCache(AssetKey<T> key);
/**
* Clears the asset cache.
*/
public void clearCache();
}

@ -207,6 +207,7 @@ public class DesktopAssetManager implements AssetManager {
return info;
}
@Override
public <T> T getFromCache(AssetKey<T> key) {
AssetCache cache = handler.getCache(key.getCacheType());
if (cache != null) {
@ -221,6 +222,7 @@ public class DesktopAssetManager implements AssetManager {
}
}
@Override
public <T> void addToCache(AssetKey<T> key, T asset) {
AssetCache cache = handler.getCache(key.getCacheType());
if (cache != null) {
@ -231,6 +233,7 @@ public class DesktopAssetManager implements AssetManager {
}
}
@Override
public <T> boolean deleteFromCache(AssetKey<T> key) {
AssetCache cache = handler.getCache(key.getCacheType());
if (cache != null) {
@ -240,6 +243,7 @@ public class DesktopAssetManager implements AssetManager {
}
}
@Override
public void clearCache(){
handler.clearCache();
if (logger.isLoggable(Level.FINER)){
@ -248,13 +252,110 @@ public class DesktopAssetManager implements AssetManager {
}
/**
* <font color="red">Thread-safe.</font>
*
* @param <T>
* @param key
* @return the loaded asset
* Loads an asset that has already been located.
* @param <T> The asset type
* @param key The asset key
* @param info The AssetInfo from the locator
* @param proc AssetProcessor to use, or null to disable processing
* @param cache The cache to store the asset in, or null to disable caching
* @return The loaded asset
*
* @throws AssetLoadException If failed to load asset due to exception or
* other error.
*/
protected <T> T loadLocatedAsset(AssetKey<T> key, AssetInfo info, AssetProcessor proc, AssetCache cache) {
AssetLoader loader = handler.aquireLoader(key);
Object obj;
try {
handler.establishParentKey(key);
obj = loader.load(info);
} catch (IOException ex) {
throw new AssetLoadException("An exception has occured while loading asset: " + key, ex);
} finally {
handler.releaseParentKey(key);
}
if (obj == null) {
throw new AssetLoadException("Error occured while loading asset \""
+ key + "\" using " + loader.getClass().getSimpleName());
} else {
if (logger.isLoggable(Level.FINER)) {
logger.log(Level.FINER, "Loaded {0} with {1}",
new Object[]{key, loader.getClass().getSimpleName()});
}
if (proc != null) {
// do processing on asset before caching
obj = proc.postProcess(key, obj);
}
if (cache != null) {
// At this point, obj should be of type T
cache.addToCache(key, (T) obj);
}
for (AssetEventListener listener : eventListeners) {
listener.assetLoaded(key);
}
return (T) obj;
}
}
/**
* Clones the asset using the given processor and registers the clone
* with the cache.
*
* @param <T> The asset type
* @param key The asset key
* @param obj The asset to clone / register, must implement
* {@link CloneableSmartAsset}.
* @param proc The processor which will generate the clone, cannot be null
* @param cache The cache to register the clone with, cannot be null.
* @return The cloned asset, cannot be the same as the given asset since
* it is a clone.
*
* @throws IllegalStateException If asset does not implement
* {@link CloneableSmartAsset}, if the cache is null, or if the
* processor did not clone the asset.
*/
public <T> T loadAsset(AssetKey<T> key){
protected <T> T registerAndCloneSmartAsset(AssetKey<T> key, T obj, AssetProcessor proc, AssetCache cache) {
// object obj is the original asset
// create an instance for user
T clone = (T) obj;
if (proc == null) {
throw new IllegalStateException("Asset implements "
+ "CloneableSmartAsset but doesn't "
+ "have processor to handle cloning");
} else {
clone = (T) proc.createClone(obj);
if (cache != null && clone != obj) {
cache.registerAssetClone(key, clone);
} else {
throw new IllegalStateException("Asset implements "
+ "CloneableSmartAsset but doesn't have cache or "
+ "was not cloned");
}
}
return clone;
}
@Override
public <T> T loadAssetFromStream(AssetKey<T> key, InputStream inputStream) {
if (key == null) {
throw new IllegalArgumentException("key cannot be null");
}
for (AssetEventListener listener : eventListeners){
listener.assetRequested(key);
}
AssetProcessor proc = handler.getProcessor(key.getProcessorType());
StreamAssetInfo info = new StreamAssetInfo(this, key, inputStream);
return loadLocatedAsset(key, info, proc, null);
}
@Override
public <T> T loadAsset(AssetKey<T> key){
if (key == null)
throw new IllegalArgumentException("key cannot be null");
@ -268,7 +369,6 @@ public class DesktopAssetManager implements AssetManager {
Object obj = cache != null ? cache.getFromCache(key) : null;
if (obj == null){
// Asset not in cache, load it from file system.
AssetLoader loader = handler.aquireLoader(key);
AssetInfo info = handler.tryLocate(key);
if (info == null){
if (handler.getParentKey() != null){
@ -282,59 +382,16 @@ public class DesktopAssetManager implements AssetManager {
}
throw new AssetNotFoundException(key.toString());
}
try {
handler.establishParentKey(key);
obj = loader.load(info);
} catch (IOException ex) {
throw new AssetLoadException("An exception has occured while loading asset: " + key, ex);
} finally {
handler.releaseParentKey(key);
}
if (obj == null){
throw new AssetLoadException("Error occured while loading asset \"" + key + "\" using " + loader.getClass().getSimpleName());
}else{
if (logger.isLoggable(Level.FINER)){
logger.log(Level.FINER, "Loaded {0} with {1}",
new Object[]{key, loader.getClass().getSimpleName()});
}
if (proc != null){
// do processing on asset before caching
obj = proc.postProcess(key, obj);
}
if (cache != null){
// At this point, obj should be of type T
cache.addToCache(key, (T) obj);
}
for (AssetEventListener listener : eventListeners){
listener.assetLoaded(key);
}
}
obj = loadLocatedAsset(key, info, proc, cache);
}
// object obj is the original asset
// create an instance for user
T clone = (T) obj;
if (clone instanceof CloneableSmartAsset){
if (proc == null){
throw new IllegalStateException("Asset implements "
+ "CloneableSmartAsset but doesn't "
+ "have processor to handle cloning");
}else{
clone = (T) proc.createClone(obj);
if (cache != null && clone != obj){
cache.registerAssetClone(key, clone);
} else{
throw new IllegalStateException("Asset implements "
+ "CloneableSmartAsset but doesn't have cache or "
+ "was not cloned");
}
}
if (obj instanceof CloneableSmartAsset) {
clone = registerAndCloneSmartAsset(key, clone, proc, cache);
}
return clone;
}

@ -0,0 +1,60 @@
/*
* Copyright (c) 2009-2015 jMonkeyEngine
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jme3.asset;
import java.io.InputStream;
/**
* An {@link AssetInfo} wrapper for {@link InputStream InputStreams}.
*
* @author Kirill Vainer
*/
public class StreamAssetInfo extends AssetInfo {
private boolean alreadyOpened;
private final InputStream inputStream;
public StreamAssetInfo(AssetManager assetManager, AssetKey<?> assetKey, InputStream inputStream) {
super(assetManager, assetKey);
this.inputStream = inputStream;
}
@Override
public InputStream openStream() {
if (alreadyOpened) {
throw new IllegalStateException("Stream already opened");
}
alreadyOpened = true;
return inputStream;
}
}
Loading…
Cancel
Save