- Clean up AssetDataObject / SpatialAssetDataObject / AssetData, add javadoc
- Add AssetDataPropertyChangeListener interface  and functionality
- Improve storage of ORIGINAL_XXX data
- Add SpatialUtil

git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@10303 75d07b2b-3a1a-0410-a2c5-0572b91ccdca
3.0
nor..67 12 years ago
parent 9a9e9a8d5a
commit 940e5315cf
  1. 61
      sdk/jme3-core/src/com/jme3/gde/core/assets/AssetData.java
  2. 102
      sdk/jme3-core/src/com/jme3/gde/core/assets/AssetDataObject.java
  3. 43
      sdk/jme3-core/src/com/jme3/gde/core/assets/AssetDataPropertyChangeListener.java
  4. 59
      sdk/jme3-core/src/com/jme3/gde/core/assets/SpatialAssetDataObject.java
  5. 216
      sdk/jme3-core/src/com/jme3/gde/core/util/SpatialUtil.java

@ -32,13 +32,16 @@
package com.jme3.gde.core.assets;
import com.jme3.asset.AssetKey;
import com.jme3.export.Savable;
import java.io.BufferedOutputStream;
import java.io.BufferedInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.logging.Level;
@ -62,6 +65,7 @@ import org.openide.util.Mutex.Action;
public class AssetData {
private static final Logger logger = Logger.getLogger(AssetData.class.getName());
private final List<AssetDataPropertyChangeListener> listeners = new ArrayList<AssetDataPropertyChangeListener>();
private final Mutex propsMutex = new Mutex();
private final Properties props = new Properties();
private AssetDataObject file;
@ -81,14 +85,28 @@ public class AssetData {
this.extension = extension;
}
public void setExtension(String extension) {
this.extension = extension;
}
/**
* Sets the extension of the assetData properties file, normally not
* necessary to use this, it will be .[originalsuffix]data, for example
* .j3odata.
*
* @param extension
*/
// public void setExtension(String extension) {
// this.extension = extension;
// }
public AssetKey<?> getAssetKey() {
return file.getAssetKey();
}
/**
* Applies the supplied keys data to the assets assetKey so it will be
* loaded with these settings next time loadAsset is actually loading the
* asset from the ProjectAssetManager.
*
* @param key
*/
public void setAssetKey(AssetKey key) {
file.setAssetKeyData(key);
}
@ -101,10 +119,23 @@ public class AssetData {
file.setSaveCookie(cookie);
}
public Object loadAsset() {
/**
* Loads the asset from the DataObject via the ProjectAssetManager in the
* lookup. Returns the currently loaded asset when it has been loaded
* already, close the asset using closeAsset().
*
* @return
*/
public Savable loadAsset() {
return file.loadAsset();
}
/**
* Saves this asset, when a saveExtension is set, saves it as a brother file
* with that extension.
*
* @throws IOException
*/
public void saveAsset() throws IOException {
file.saveAsset();
}
@ -152,6 +183,7 @@ public class AssetData {
}
});
writeProperties();
notifyListeners(key, ret, value);
return ret;
}
@ -233,4 +265,25 @@ public class AssetData {
}
});
}
protected void notifyListeners(String property, String before, String after) {
synchronized (listeners) {
for (Iterator<AssetDataPropertyChangeListener> it = listeners.iterator(); it.hasNext();) {
AssetDataPropertyChangeListener assetDataPropertyChangeListener = it.next();
assetDataPropertyChangeListener.assetDataPropertyChanged(property, before, after);
}
}
}
public void addPropertyChangeListener(AssetDataPropertyChangeListener listener) {
synchronized (listeners) {
listeners.add(listener);
}
}
public void removePropertyChangeListener(AssetDataPropertyChangeListener listener) {
synchronized (listeners) {
listeners.remove(listener);
}
}
}

@ -33,13 +33,16 @@ package com.jme3.gde.core.assets;
import com.jme3.asset.AssetEventListener;
import com.jme3.asset.AssetKey;
import com.jme3.asset.BlenderKey;
import com.jme3.export.Savable;
import com.jme3.export.binary.BinaryExporter;
import com.jme3.gde.core.scene.ApplicationLogHandler.LogLevel;
import com.jme3.gde.core.scene.SceneApplication;
import com.jme3.scene.Spatial;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
@ -75,8 +78,15 @@ import org.openide.util.lookup.ProxyLookup;
public class AssetDataObject extends MultiDataObject {
protected static final Logger logger = Logger.getLogger(AssetDataObject.class.getName());
protected final Lookup lookup;
protected final InstanceContent lookupContents = new InstanceContent();
protected final AbstractLookup contentLookup;
protected final Lookup lookup;
protected final AssetData assetData;
protected final ProjectAssetManager assetManager;
protected final AssetListListener listListener;
protected final List<FileObject> assetList = new LinkedList<FileObject>();
protected final List<AssetKey> assetKeyList = new LinkedList<AssetKey>();
protected final List<AssetKey> failedList = new LinkedList<AssetKey>();
protected SaveCookie saveCookie = new SaveCookie() {
public void save() throws IOException {
//TODO: On OpenGL thread? -- safest way.. with get()?
@ -92,23 +102,20 @@ public class AssetDataObject extends MultiDataObject {
protected AssetKey assetKey;
protected Savable savable;
protected String saveExtension;
protected AbstractLookup contentLookup;
protected AssetListListener listListener;
protected List<FileObject> assetList = new LinkedList<FileObject>();
protected List<AssetKey> assetKeyList = new LinkedList<AssetKey>();
protected List<AssetKey> failedList = new LinkedList<AssetKey>();
public AssetDataObject(FileObject pf, MultiFileLoader loader) throws DataObjectExistsException, IOException {
super(pf, loader);
contentLookup = new AbstractLookup(getLookupContents());
lookupContents.add(new AssetData(this));
contentLookup = new AbstractLookup(lookupContents);
assetData = new AssetData(this);
lookupContents.add(assetData);
lookup = new ProxyLookup(getCookieSet().getLookup(), contentLookup);
listListener = new AssetListListener(this, assetList, assetKeyList, failedList);
assetManager = findAssetManager();
//assign savecookie (same as method)
setSaveCookie(saveCookie);
findAssetManager();
}
protected void findAssetManager() {
private ProjectAssetManager findAssetManager() {
FileObject file = getPrimaryFile();
ProjectManager pm = ProjectManager.getDefault();
while (file != null) {
@ -119,7 +126,7 @@ public class AssetDataObject extends MultiDataObject {
ProjectAssetManager mgr = project.getLookup().lookup(ProjectAssetManager.class);
if (mgr != null) {
getLookupContents().add(mgr);
return;
return mgr;
}
}
} catch (IOException ex) {
@ -128,7 +135,7 @@ public class AssetDataObject extends MultiDataObject {
}
file = file.getParent();
}
// getLookupContents().add(new ProjectAssetManager(file.getParent()));
return null;
}
@Override
@ -163,9 +170,16 @@ public class AssetDataObject extends MultiDataObject {
setModified(false);
}
/**
* Loads the asset from the DataObject via the ProjectAssetManager in the
* lookup. Returns the currently loaded asset when it has been loaded
* already, close the asset using closeAsset().
*
* @return
*/
public synchronized Savable loadAsset() {
if (isModified() && savable != null) {
return savable;
if (savable != null) {
return (Spatial) savable;
}
ProjectAssetManager mgr = getLookup().lookup(ProjectAssetManager.class);
if (mgr == null) {
@ -190,6 +204,12 @@ public class AssetDataObject extends MultiDataObject {
return savable;
}
/**
* Saves this asset, when a saveExtension is set, saves it as a brother file
* with that extension.
*
* @throws IOException
*/
public synchronized void saveAsset() throws IOException {
if (savable == null) {
logger.log(Level.WARNING, "Trying to write asset failed, asset data null!\nImport failed?");
@ -222,14 +242,47 @@ public class AssetDataObject extends MultiDataObject {
}
}
progressHandle.finish();
logger.log(LogLevel.USERINFO, "File {0} saved successfully", getPrimaryFile().getNameExt());
setModified(false);
logger.log(LogLevel.USERINFO, "File {0} saved successfully", getPrimaryFile().getNameExt());
}
/**
* Closes this asset so that loadAsset will cause it to be loaded
*/
public synchronized void closeAsset() {
ProjectAssetManager mgr = getLookup().lookup(ProjectAssetManager.class);
if (mgr != null) {
logger.log(Level.INFO, "Closing asset {0}, deleting from cache.", getName());
mgr.deleteFromCache(getAssetKey());
//delete referenced assets too
for (Iterator<AssetKey> it = assetKeyList.iterator(); it.hasNext();) {
AssetKey assetKey1 = it.next();
mgr.deleteFromCache(assetKey1);
}
} else {
logger.log(Level.WARNING, "Closing asset {0} with no ProjectAssetManager assigned..?", getName());
}
savable = null;
}
/**
* Returns the AssetKey of this asset type. When extending AssetDataObject
* or a subtype the class should override this so the key type and
* properties can be recognized properly:
* <pre>
* public synchronized MyKeyType getAssetKey() {
* //return key if already set
* if(super.getAssetKey() instanceof MyKeyType){
* return (MyKeyType)assetKey;
* }
* //set own key type and return
* assetKey = new MyKeyType(super.getAssetKey().getName());
* return (MyKeyType)assetKey;
* }
* </pre>
*
* @return
*/
public synchronized AssetKey<?> getAssetKey() {
if (assetKey == null) {
ProjectAssetManager mgr = getLookup().lookup(ProjectAssetManager.class);
@ -242,6 +295,13 @@ public class AssetDataObject extends MultiDataObject {
return assetKey;
}
/**
* Applies the supplied keys data to the assets assetKey so it will be
* loaded with these settings next time loadAsset is actually loading the
* asset from the ProjectAssetManager.
*
* @param key
*/
public synchronized void setAssetKeyData(AssetKey key) {
try {
BeanUtils.copyProperties(getAssetKey(), key);
@ -287,9 +347,9 @@ public class AssetDataObject extends MultiDataObject {
if (pm == null || loadingThread != Thread.currentThread()) {
return;
}
FileObject obj = pm.getAssetFileObject(ak);
if (obj != null && !assetList.contains(obj)) {
assetList.add(obj);
FileObject fObj = pm.getAssetFileObject(ak);
if (fObj != null && !assetList.contains(fObj)) {
assetList.add(fObj);
assetKeyList.add(ak);
}
}
@ -299,9 +359,9 @@ public class AssetDataObject extends MultiDataObject {
if (pm == null || loadingThread != Thread.currentThread()) {
return;
}
FileObject obj = pm.getAssetFileObject(ak1);
if (obj != null && assetList.contains(obj)) {
assetList.remove(obj);
FileObject fObj = pm.getAssetFileObject(ak1);
if (fObj != null && assetList.contains(fObj)) {
assetList.remove(fObj);
assetKeyList.remove(ak1);
}
if (!failedList.contains(ak1)) {

@ -0,0 +1,43 @@
/*
* Copyright (c) 2003-2012 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.gde.core.assets;
/**
* Allows listening for property changes in AssetData objects. The call is not
* necessarily coming from the main AWT thread but from the import thread.
*
* @author normenhansen
*/
public interface AssetDataPropertyChangeListener {
public void assetDataPropertyChanged(String property, String before, String after);
}

@ -33,11 +33,9 @@ package com.jme3.gde.core.assets;
import com.jme3.asset.AssetKey;
import com.jme3.asset.ModelKey;
import com.jme3.scene.Geometry;
import com.jme3.scene.SceneGraphVisitorAdapter;
import com.jme3.gde.core.util.SpatialUtil;
import com.jme3.scene.Spatial;
import java.io.IOException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.openide.DialogDisplayer;
@ -81,7 +79,7 @@ public class SpatialAssetDataObject extends AssetDataObject {
@Override
public synchronized Spatial loadAsset() {
if (isModified() && savable != null) {
if (savable != null) {
return (Spatial) savable;
}
ProjectAssetManager mgr = getLookup().lookup(ProjectAssetManager.class);
@ -98,7 +96,7 @@ public class SpatialAssetDataObject extends AssetDataObject {
listListener.stop();
savable = spatial;
if (!(this instanceof BinaryModelDataObject)) {
storeOriginalPathUserData();
SpatialUtil.storeOriginalPathUserData(spatial);
}
lock.releaseLock();
return spatial;
@ -135,56 +133,15 @@ public class SpatialAssetDataObject extends AssetDataObject {
AssetData properties = targetModel.getLookup().lookup(AssetData.class);
if (properties != null) {
if (properties.getProperty("ORIGINAL_PATH") == null) {
properties.setProperty("ORIGINAL_PATH", mgr.getRelativeAssetPath(outFile.getPath()));
String path = mgr.getRelativeAssetPath(getPrimaryFile().getPath());
properties.setProperty("ORIGINAL_PATH", path);
logger.log(Level.INFO, "Set original path for {0} to {1}", new Object[]{getName(), path});
}
} else {
logger.log(Level.WARNING, "New object {0} has no AssetData?", getName());
}
} catch (Exception ex) {
Exceptions.printStackTrace(ex);
}
}
/**
* Stores ORIGINAL_NAME and ORIGINAL_PATH UserData to all Geometry in
* loaded spatial
*/
protected void storeOriginalPathUserData() {
final ArrayList<String> geomMap = new ArrayList<String>();
Spatial spat = (Spatial) savable;
if (spat != null) {
spat.depthFirstTraversal(new SceneGraphVisitorAdapter() {
@Override
public void visit(Geometry geom) {
StringBuilder geometryIdentifier = new StringBuilder();
Spatial curSpat = geom;
String geomName = curSpat.getName();
if (geomName == null) {
logger.log(Level.WARNING, "Null geometry name!");
geomName = "null";
}
geom.setUserData("ORIGINAL_NAME", geomName);
logger.log(Level.FINE, "Set ORIGINAL_NAME for {0}", geomName);
while (curSpat != null) {
String name = curSpat.getName();
if (name == null) {
logger.log(Level.WARNING, "Null spatial name!");
name = "null";
}
geometryIdentifier.insert(0, name);
geometryIdentifier.insert(0, '/');
curSpat = curSpat.getParent();
}
String id = geometryIdentifier.toString();
if (geomMap.contains(id)) {
logger.log(Level.WARNING, "Cannot create unique name for Geometry {0}: {1}", new Object[]{geom, id});
}
geomMap.add(id);
geom.setUserData("ORIGINAL_PATH", id);
logger.log(Level.FINE, "Set ORIGINAL_PATH for {0}", id);
super.visit(geom);
}
});
} else {
logger.log(Level.SEVERE, "No geometry available when trying to scan initial geometry configuration");
}
}
}

@ -0,0 +1,216 @@
/*
* Copyright (c) 2003-2012 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.gde.core.util;
import com.jme3.gde.core.scene.ApplicationLogHandler.LogLevel;
import com.jme3.scene.Geometry;
import com.jme3.scene.SceneGraphVisitor;
import com.jme3.scene.SceneGraphVisitorAdapter;
import com.jme3.scene.Spatial;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Various utilities, mostly for operating on Spatials recursively.
*
* @author normenhansen
*/
public class SpatialUtil {
private static final Logger logger = Logger.getLogger(SpatialUtil.class.getName());
//TODO: use these variables
public static final String ORIGINAL_NAME = "ORIGINAL_NAME";
public static final String ORIGINAL_PATH = "ORIGINAL_PATH";
/**
* Gets a "pathname" for the given Spatial, combines the Spatials and
* parents names to make a long name. This "path" is stored in geometry
* after the first import for example.
*
* @param spat
* @return
*/
public static String getSpatialPath(Spatial spat) {
StringBuilder geometryIdentifier = new StringBuilder();
while (spat != null) {
String name = spat.getName();
if (name == null) {
logger.log(Level.WARNING, "Null spatial name!");
name = "null";
}
geometryIdentifier.insert(0, name);
geometryIdentifier.insert(0, '/');
spat = spat.getParent();
}
String id = geometryIdentifier.toString();
return id;
}
/**
* Stores ORIGINAL_NAME and ORIGINAL_PATH UserData to all Geometry in loaded
* spatial
*
* @param spat
*/
public static void storeOriginalPathUserData(Spatial spat) {
//TODO: only stores for geometry atm
final ArrayList<String> geomMap = new ArrayList<String>();
if (spat != null) {
spat.depthFirstTraversal(new SceneGraphVisitorAdapter() {
@Override
public void visit(Geometry geom) {
Spatial curSpat = geom;
String geomName = curSpat.getName();
if (geomName == null) {
logger.log(Level.WARNING, "Null geometry name!");
geomName = "null";
}
geom.setUserData("ORIGINAL_NAME", geomName);
logger.log(Level.FINE, "Set ORIGINAL_NAME for {0}", geomName);
String id = SpatialUtil.getSpatialPath(curSpat);
if (geomMap.contains(id)) {
logger.log(Level.WARNING, "Cannot create unique name for Geometry {0}: {1}", new Object[]{geom, id});
}
geomMap.add(id);
geom.setUserData("ORIGINAL_PATH", id);
logger.log(Level.FINE, "Set ORIGINAL_PATH for {0}", id);
super.visit(geom);
}
});
} else {
logger.log(Level.SEVERE, "No geometry available when trying to scan initial geometry configuration");
}
}
/**
* Finds a previously marked spatial in the supplied root Spatial, creates
* the name and path to be looked for from the given needle Spatial.
*
* @param root
* @param needle
* @return
*/
public static Spatial findTaggedSpatial(final Spatial root, final Spatial needle) {
if (needle == null) {
logger.log(Level.WARNING, "Trying to find null needle for {0}", root);
return null;
}
final String name = needle.getName();
final String path = getSpatialPath(needle);
if (name == null || path == null) {
logger.log(Level.INFO, "Trying to find tagged spatial with null name spatial for {0}.", root);
}
final Class clazz = needle.getClass();
String rootName = root.getUserData("ORIGINAL_NAME");
String rootPath = root.getUserData("ORIGINAL_PATH");
if (name.equals(rootName) && path.equals(rootPath)) {
return root;
}
final SpatialHolder holder = new SpatialHolder();
root.depthFirstTraversal(new SceneGraphVisitor() {
public void visit(Spatial spatial) {
String spName = spatial.getUserData("ORIGINAL_NAME");
String spPath = spatial.getUserData("ORIGINAL_PATH");
if (name.equals(spName) && path.equals(spPath) && clazz.isInstance(spatial)) {
if (holder.spatial == null) {
holder.spatial = spatial;
} else {
logger.log(Level.WARNING, "Found spatial {0} twice in {1}", new Object[]{path, root});
}
}
}
});
return holder.spatial;
}
/**
* Finds a spatial in the given Spatial tree with the specified name and
* path, the path and name are constructed from the given (sub-)spatial(s)
* and is not read from the UserData of the objects. This is mainly used to
* check if the original spatial still exists in the original file.
*
* @param root
* @param name
* @param path
*/
public static Spatial findSpatial(final Spatial root, final String name, final String path) {
if (name.equals(root.getName()) && getSpatialPath(root).equals(path)) {
return root;
}
final SpatialHolder holder = new SpatialHolder();
root.depthFirstTraversal(new SceneGraphVisitor() {
public void visit(Spatial spatial) {
if (name.equals(spatial.getName()) && getSpatialPath(spatial).equals(path)) {
if (holder.spatial == null) {
holder.spatial = spatial;
} else {
logger.log(Level.WARNING, "Found spatial {0} twice in {1}", new Object[]{path, root});
}
}
}
});
return holder.spatial;
}
public static void updateOriginalMeshData(final Spatial root, final Spatial original) {
original.depthFirstTraversal(new SceneGraphVisitorAdapter() {
@Override
public void visit(Geometry geom) {
//will always return same class type, so casting is safe
Geometry spat = (Geometry) findTaggedSpatial(root, geom);
if (spat != null) {
spat.setMesh(geom.getMesh().deepClone());
logger.log(LogLevel.USERINFO, "Updated mesh for geometry {0}", geom.getName());
} else {
// addNewOriginal()
}
}
});
return;
}
private void addNewOriginalGeometry(final Spatial root, final Geometry original) {
return;
}
public void clearRemovedOriginals(final Spatial root, final Spatial original) {
return;
}
private static class SpatialHolder {
Spatial spatial;
}
}
Loading…
Cancel
Save