git-svn-id: https://jmonkeyengine.googlecode.com/svn/trunk@7675 75d07b2b-3a1a-0410-a2c5-0572b91ccdca3.0
parent
c410fae0a6
commit
fd2184784c
@ -0,0 +1,441 @@ |
||||
package com.jme3.scene.plugins.blender.utils; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.logging.Logger; |
||||
|
||||
import sun.reflect.generics.reflectiveObjects.NotImplementedException; |
||||
|
||||
import com.jme3.export.InputCapsule; |
||||
import com.jme3.export.JmeExporter; |
||||
import com.jme3.export.JmeImporter; |
||||
import com.jme3.export.OutputCapsule; |
||||
import com.jme3.export.Savable; |
||||
import com.jme3.scene.plugins.blender.data.FileBlockHeader; |
||||
import com.jme3.scene.plugins.blender.data.Structure; |
||||
import com.jme3.scene.plugins.blender.exception.BlenderFileException; |
||||
|
||||
/** |
||||
* The blender object's custom properties. |
||||
* This class is valid for all versions of blender. |
||||
* @author Marcin Roguski (Kaelthas) |
||||
*/ |
||||
public class Properties implements Cloneable, Savable { |
||||
private static final Logger LOGGER = Logger.getLogger(Properties.class.getName()); |
||||
|
||||
// property type
|
||||
public static final int IDP_STRING = 0; |
||||
public static final int IDP_INT = 1; |
||||
public static final int IDP_FLOAT = 2; |
||||
public static final int IDP_ARRAY = 5; |
||||
public static final int IDP_GROUP = 6; |
||||
// public static final int IDP_ID = 7;//this is not implemented in blender (yet)
|
||||
public static final int IDP_DOUBLE = 8; |
||||
// the following are valid for blender 2.5x+
|
||||
public static final int IDP_IDPARRAY = 9; |
||||
public static final int IDP_NUMTYPES = 10; |
||||
|
||||
protected static final String RNA_PROPERTY_NAME = "_RNA_UI"; |
||||
/** Default name of the property (used if the name is not specified in blender file). */ |
||||
protected static final String DEFAULT_NAME = "Unnamed property"; |
||||
|
||||
/** The name of the property. */ |
||||
private String name; |
||||
/** The type of the property. */ |
||||
private int type; |
||||
/** The subtype of the property. Defines the type of array's elements. */ |
||||
private int subType; |
||||
/** The value of the property. */ |
||||
private Object value; |
||||
/** The description of the property. */ |
||||
private String description; |
||||
|
||||
/** |
||||
* This method loads the property from the belnder file. |
||||
* @param idPropertyStructure |
||||
* the ID structure constining the property |
||||
* @param dataRepository |
||||
* the data repository |
||||
* @throws BlenderFileException |
||||
* an exception is thrown when the belnder file is somehow invalid |
||||
*/ |
||||
public void load(Structure idPropertyStructure, DataRepository dataRepository) throws BlenderFileException { |
||||
name = idPropertyStructure.getFieldValue("name").toString(); |
||||
if (name == null || name.length() == 0) { |
||||
name = DEFAULT_NAME; |
||||
} |
||||
subType = ((Number) idPropertyStructure.getFieldValue("subtype")).intValue(); |
||||
type = ((Number) idPropertyStructure.getFieldValue("type")).intValue(); |
||||
|
||||
// reading the data
|
||||
Structure data = (Structure) idPropertyStructure.getFieldValue("data"); |
||||
int len = ((Number) idPropertyStructure.getFieldValue("len")).intValue(); |
||||
switch (type) { |
||||
case IDP_STRING: { |
||||
Pointer pointer = (Pointer) data.getFieldValue("pointer"); |
||||
BlenderInputStream bis = dataRepository.getInputStream(); |
||||
FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pointer.getOldMemoryAddress()); |
||||
bis.setPosition(dataFileBlock.getBlockPosition()); |
||||
value = bis.readString(); |
||||
break; |
||||
} |
||||
case IDP_INT: |
||||
int intValue = ((Number) data.getFieldValue("val")).intValue(); |
||||
value = Integer.valueOf(intValue); |
||||
break; |
||||
case IDP_FLOAT: |
||||
int floatValue = ((Number) data.getFieldValue("val")).intValue(); |
||||
value = Float.valueOf(Float.intBitsToFloat(floatValue)); |
||||
break; |
||||
case IDP_ARRAY: { |
||||
Pointer pointer = (Pointer) data.getFieldValue("pointer"); |
||||
BlenderInputStream bis = dataRepository.getInputStream(); |
||||
FileBlockHeader dataFileBlock = dataRepository.getFileBlock(pointer.getOldMemoryAddress()); |
||||
bis.setPosition(dataFileBlock.getBlockPosition()); |
||||
int elementAmount = dataFileBlock.getSize(); |
||||
switch (subType) { |
||||
case IDP_INT: |
||||
elementAmount /= 4; |
||||
int[] intList = new int[elementAmount]; |
||||
for (int i = 0; i < elementAmount; ++i) { |
||||
intList[i] = bis.readInt(); |
||||
} |
||||
value = intList; |
||||
break; |
||||
case IDP_FLOAT: |
||||
elementAmount /= 4; |
||||
float[] floatList = new float[elementAmount]; |
||||
for (int i = 0; i < elementAmount; ++i) { |
||||
floatList[i] = bis.readFloat(); |
||||
} |
||||
value = floatList; |
||||
break; |
||||
case IDP_DOUBLE: |
||||
elementAmount /= 8; |
||||
double[] doubleList = new double[elementAmount]; |
||||
for (int i = 0; i < elementAmount; ++i) { |
||||
doubleList[i] = bis.readDouble(); |
||||
} |
||||
value = doubleList; |
||||
break; |
||||
default: |
||||
throw new IllegalStateException("Invalid array subtype: " + subType); |
||||
} |
||||
} |
||||
case IDP_GROUP: |
||||
Structure group = (Structure) data.getFieldValue("group"); |
||||
List<Structure> dataList = group.evaluateListBase(dataRepository); |
||||
List<Properties> subProperties = new ArrayList<Properties>(len); |
||||
for (Structure d : dataList) { |
||||
Properties properties = new Properties(); |
||||
properties.load(d, dataRepository); |
||||
subProperties.add(properties); |
||||
} |
||||
value = subProperties; |
||||
break; |
||||
case IDP_DOUBLE: |
||||
int doublePart1 = ((Number) data.getFieldValue("val")).intValue(); |
||||
int doublePart2 = ((Number) data.getFieldValue("val2")).intValue(); |
||||
long doubleVal = (long) doublePart2 << 32 | doublePart1; |
||||
value = Double.valueOf(Double.longBitsToDouble(doubleVal)); |
||||
break; |
||||
case IDP_IDPARRAY: { |
||||
Pointer pointer = (Pointer) data.getFieldValue("pointer"); |
||||
List<Structure> arrays = pointer.fetchData(dataRepository.getInputStream()); |
||||
List<Object> result = new ArrayList<Object>(arrays.size()); |
||||
Properties temp = new Properties(); |
||||
for (Structure array : arrays) { |
||||
temp.load(array, dataRepository); |
||||
result.add(temp.value); |
||||
} |
||||
this.value = result; |
||||
break; |
||||
} |
||||
case IDP_NUMTYPES: |
||||
throw new NotImplementedException(); |
||||
// case IDP_ID://not yet implemented in blender
|
||||
// return null;
|
||||
default: |
||||
throw new IllegalStateException("Unknown custom property type: " + type); |
||||
} |
||||
this.completeLoading(); |
||||
} |
||||
|
||||
/** |
||||
* This method returns the name of the property. |
||||
* @return the name of the property |
||||
*/ |
||||
public String getName() { |
||||
return name; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the description of the property. |
||||
* @return the description of the property |
||||
*/ |
||||
public String getDescription() { |
||||
return description; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the type of the property. |
||||
* @return the type of the property |
||||
*/ |
||||
public int getType() { |
||||
return type; |
||||
} |
||||
|
||||
/** |
||||
* This method returns the value of the property. |
||||
* The type of the value depends on the type of the property. |
||||
* @return the value of the property |
||||
*/ |
||||
public Object getValue() { |
||||
return value; |
||||
} |
||||
|
||||
@Override |
||||
public String toString() { |
||||
StringBuilder sb = new StringBuilder(); |
||||
this.append(sb, new StringBuilder()); |
||||
return sb.toString(); |
||||
} |
||||
|
||||
/** |
||||
* This method appends the data of the property to the given string buffer. |
||||
* @param sb |
||||
* string buffer |
||||
* @param indent |
||||
* indent buffer |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
private void append(StringBuilder sb, StringBuilder indent) { |
||||
sb.append(indent).append("name: ").append(name).append("\n\r"); |
||||
sb.append(indent).append("type: ").append(type).append("\n\r"); |
||||
sb.append(indent).append("subType: ").append(subType).append("\n\r"); |
||||
sb.append(indent).append("description: ").append(description).append("\n\r"); |
||||
indent.append('\t'); |
||||
sb.append(indent).append("value: "); |
||||
if (value instanceof Properties) { |
||||
((Properties) value).append(sb, indent); |
||||
} else if (value instanceof List) { |
||||
for (Object v : (List<Object>) value) { |
||||
if (v instanceof Properties) { |
||||
sb.append(indent).append("{\n\r"); |
||||
indent.append('\t'); |
||||
((Properties) v).append(sb, indent); |
||||
indent.deleteCharAt(indent.length() - 1); |
||||
sb.append(indent).append("}\n\r"); |
||||
} else { |
||||
sb.append(v); |
||||
} |
||||
} |
||||
} else { |
||||
sb.append(value); |
||||
} |
||||
sb.append("\n\r"); |
||||
indent.deleteCharAt(indent.length() - 1); |
||||
} |
||||
|
||||
/** |
||||
* This method should be called after the properties loading. |
||||
* It loads the properties from the _RNA_UI property and removes this property from the |
||||
* result list. |
||||
*/ |
||||
@SuppressWarnings("unchecked") |
||||
protected void completeLoading() { |
||||
if (this.type == IDP_GROUP) { |
||||
List<Properties> groupProperties = (List<Properties>) this.value; |
||||
Properties rnaUI = null; |
||||
for (Properties properties : groupProperties) { |
||||
if (properties.name.equals(RNA_PROPERTY_NAME) && properties.type == IDP_GROUP) { |
||||
rnaUI = properties; |
||||
break; |
||||
} |
||||
} |
||||
if (rnaUI != null) { |
||||
// removing the RNA from the result list
|
||||
groupProperties.remove(rnaUI); |
||||
|
||||
// loading the descriptions
|
||||
Map<String, String> descriptions = new HashMap<String, String>(groupProperties.size()); |
||||
List<Properties> propertiesRNA = (List<Properties>) rnaUI.value; |
||||
for (Properties properties : propertiesRNA) { |
||||
String name = properties.name; |
||||
String description = null; |
||||
List<Properties> rnaData = (List<Properties>) properties.value; |
||||
for (Properties rna : rnaData) { |
||||
if ("description".equalsIgnoreCase(rna.name)) { |
||||
description = (String) rna.value; |
||||
break; |
||||
} |
||||
} |
||||
descriptions.put(name, description); |
||||
} |
||||
|
||||
// applying the descriptions
|
||||
for (Properties properties : groupProperties) { |
||||
properties.description = descriptions.get(properties.name); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
@SuppressWarnings("rawtypes") |
||||
public void write(JmeExporter ex) throws IOException { |
||||
OutputCapsule oc = ex.getCapsule(this); |
||||
oc.write(name, "name", DEFAULT_NAME); |
||||
oc.write(type, "type", 0); |
||||
oc.write(subType, "subtype", 0); |
||||
oc.write(description, "description", null); |
||||
switch (type) { |
||||
case IDP_STRING: |
||||
oc.write((String) value, "value", null); |
||||
break; |
||||
case IDP_INT: |
||||
oc.write((Integer) value, "value", 0); |
||||
break; |
||||
case IDP_FLOAT: |
||||
oc.write((Float) value, "value", 0); |
||||
break; |
||||
case IDP_ARRAY: |
||||
switch (subType) { |
||||
case IDP_INT: |
||||
oc.write((int[]) value, "value", null); |
||||
break; |
||||
case IDP_FLOAT: |
||||
oc.write((float[]) value, "value", null); |
||||
break; |
||||
case IDP_DOUBLE: |
||||
oc.write((double[]) value, "value", null); |
||||
break; |
||||
default: |
||||
LOGGER.warning("Cannot save the property's value! Invalid array subtype! Property: name: " + name + "; subtype: " + subType); |
||||
} |
||||
case IDP_GROUP: |
||||
oc.write((Properties) value, "value", null); |
||||
break; |
||||
case IDP_DOUBLE: |
||||
oc.write((Double) value, "value", 0); |
||||
break; |
||||
case IDP_IDPARRAY: |
||||
oc.writeSavableArrayList((ArrayList) value, "value", null); |
||||
break; |
||||
case IDP_NUMTYPES: |
||||
LOGGER.warning("Numtypes value not supported! Cannot write it!"); |
||||
break; |
||||
// case IDP_ID://not yet implemented in blender
|
||||
// break;
|
||||
default: |
||||
LOGGER.warning("Cannot save the property's value! Invalid type! Property: name: " + name + "; type: " + type); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public void read(JmeImporter im) throws IOException { |
||||
InputCapsule ic = im.getCapsule(this); |
||||
name = ic.readString("name", DEFAULT_NAME); |
||||
type = ic.readInt("type", 0); |
||||
subType = ic.readInt("subtype", 0); |
||||
description = ic.readString("description", null); |
||||
switch (type) { |
||||
case IDP_STRING: |
||||
value = ic.readString("value", null); |
||||
break; |
||||
case IDP_INT: |
||||
value = Integer.valueOf(ic.readInt("value", 0)); |
||||
break; |
||||
case IDP_FLOAT: |
||||
value = Float.valueOf(ic.readFloat("value", 0.0f)); |
||||
break; |
||||
case IDP_ARRAY: |
||||
switch (subType) { |
||||
case IDP_INT: |
||||
value = ic.readIntArray("value", null); |
||||
break; |
||||
case IDP_FLOAT: |
||||
value = ic.readFloatArray("value", null); |
||||
break; |
||||
case IDP_DOUBLE: |
||||
value = ic.readDoubleArray("value", null); |
||||
break; |
||||
default: |
||||
LOGGER.warning("Cannot read the property's value! Invalid array subtype! Property: name: " + name + "; subtype: " + subType); |
||||
} |
||||
case IDP_GROUP: |
||||
value = ic.readSavable("value", null); |
||||
break; |
||||
case IDP_DOUBLE: |
||||
value = Double.valueOf(ic.readDouble("value", 0.0)); |
||||
break; |
||||
case IDP_IDPARRAY: |
||||
value = ic.readSavableArrayList("value", null); |
||||
break; |
||||
case IDP_NUMTYPES: |
||||
LOGGER.warning("Numtypes value not supported! Cannot read it!"); |
||||
break; |
||||
// case IDP_ID://not yet implemented in blender
|
||||
// break;
|
||||
default: |
||||
LOGGER.warning("Cannot read the property's value! Invalid type! Property: name: " + name + "; type: " + type); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public int hashCode() { |
||||
final int prime = 31; |
||||
int result = 1; |
||||
result = prime * result + (description == null ? 0 : description.hashCode()); |
||||
result = prime * result + (name == null ? 0 : name.hashCode()); |
||||
result = prime * result + subType; |
||||
result = prime * result + type; |
||||
result = prime * result + (value == null ? 0 : value.hashCode()); |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
public boolean equals(Object obj) { |
||||
if (this == obj) { |
||||
return true; |
||||
} |
||||
if (obj == null) { |
||||
return false; |
||||
} |
||||
if (this.getClass() != obj.getClass()) { |
||||
return false; |
||||
} |
||||
Properties other = (Properties) obj; |
||||
if (description == null) { |
||||
if (other.description != null) { |
||||
return false; |
||||
} |
||||
} else if (!description.equals(other.description)) { |
||||
return false; |
||||
} |
||||
if (name == null) { |
||||
if (other.name != null) { |
||||
return false; |
||||
} |
||||
} else if (!name.equals(other.name)) { |
||||
return false; |
||||
} |
||||
if (subType != other.subType) { |
||||
return false; |
||||
} |
||||
if (type != other.type) { |
||||
return false; |
||||
} |
||||
if (value == null) { |
||||
if (other.value != null) { |
||||
return false; |
||||
} |
||||
} else if (!value.equals(other.value)) { |
||||
return false; |
||||
} |
||||
return true; |
||||
} |
||||
} |
Loading…
Reference in new issue