159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapackage com.jme3.scene.plugins.blender.objects; 259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.export.*; 459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.BlenderContext; 559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.exceptions.BlenderFileException; 659b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.BlenderInputStream; 759b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.FileBlockHeader; 859b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.Pointer; 959b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport com.jme3.scene.plugins.blender.file.Structure; 1059b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.io.IOException; 1159b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.ArrayList; 1259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.HashMap; 1359b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.List; 1459b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.Map; 1559b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartaimport java.util.logging.Logger; 1659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 1759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta/** 1859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * The blender object's custom properties. 1959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This class is valid for all versions of blender. 2059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @author Marcin Roguski (Kaelthas) 2159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 2259b2e6871c65f58fdad78cd7229c292f6a177578Scott Bartapublic class Properties implements Cloneable, Savable { 2359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private static final Logger LOGGER = Logger.getLogger(Properties.class.getName()); 2459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 2559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // property type 2659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public static final int IDP_STRING = 0; 2759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public static final int IDP_INT = 1; 2859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public static final int IDP_FLOAT = 2; 2959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public static final int IDP_ARRAY = 5; 3059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public static final int IDP_GROUP = 6; 3159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // public static final int IDP_ID = 7;//this is not implemented in blender (yet) 3259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public static final int IDP_DOUBLE = 8; 3359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // the following are valid for blender 2.5x+ 3459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public static final int IDP_IDPARRAY = 9; 3559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public static final int IDP_NUMTYPES = 10; 3659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 3759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected static final String RNA_PROPERTY_NAME = "_RNA_UI"; 3859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** Default name of the property (used if the name is not specified in blender file). */ 3959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected static final String DEFAULT_NAME = "Unnamed property"; 4059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 4159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The name of the property. */ 4259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private String name; 4359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The type of the property. */ 4459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private int type; 4559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The subtype of the property. Defines the type of array's elements. */ 4659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private int subType; 4759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The value of the property. */ 4859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private Object value; 4959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** The description of the property. */ 5059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private String description; 5159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 5259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 5359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method loads the property from the belnder file. 5459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param idPropertyStructure 5559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the ID structure constining the property 5659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param blenderContext 5759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the blender context 5859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @throws BlenderFileException 5959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * an exception is thrown when the belnder file is somehow invalid 6059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 6159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void load(Structure idPropertyStructure, BlenderContext blenderContext) throws BlenderFileException { 6259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta name = idPropertyStructure.getFieldValue("name").toString(); 6359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (name == null || name.length() == 0) { 6459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta name = DEFAULT_NAME; 6559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 6659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta subType = ((Number) idPropertyStructure.getFieldValue("subtype")).intValue(); 6759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta type = ((Number) idPropertyStructure.getFieldValue("type")).intValue(); 6859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 6959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // reading the data 7059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Structure data = (Structure) idPropertyStructure.getFieldValue("data"); 7159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int len = ((Number) idPropertyStructure.getFieldValue("len")).intValue(); 7259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta switch (type) { 7359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_STRING: { 7459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Pointer pointer = (Pointer) data.getFieldValue("pointer"); 7559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta BlenderInputStream bis = blenderContext.getInputStream(); 7659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress()); 7759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bis.setPosition(dataFileBlock.getBlockPosition()); 7859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = bis.readString(); 7959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 8059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 8159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_INT: 8259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int intValue = ((Number) data.getFieldValue("val")).intValue(); 8359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = Integer.valueOf(intValue); 8459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 8559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_FLOAT: 8659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int floatValue = ((Number) data.getFieldValue("val")).intValue(); 8759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = Float.valueOf(Float.intBitsToFloat(floatValue)); 8859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 8959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_ARRAY: { 9059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Pointer pointer = (Pointer) data.getFieldValue("pointer"); 9159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta BlenderInputStream bis = blenderContext.getInputStream(); 9259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress()); 9359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta bis.setPosition(dataFileBlock.getBlockPosition()); 9459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int elementAmount = dataFileBlock.getSize(); 9559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta switch (subType) { 9659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_INT: 9759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta elementAmount /= 4; 9859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int[] intList = new int[elementAmount]; 9959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (int i = 0; i < elementAmount; ++i) { 10059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta intList[i] = bis.readInt(); 10159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 10259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = intList; 10359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 10459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_FLOAT: 10559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta elementAmount /= 4; 10659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta float[] floatList = new float[elementAmount]; 10759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (int i = 0; i < elementAmount; ++i) { 10859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta floatList[i] = bis.readFloat(); 10959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 11059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = floatList; 11159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 11259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_DOUBLE: 11359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta elementAmount /= 8; 11459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta double[] doubleList = new double[elementAmount]; 11559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (int i = 0; i < elementAmount; ++i) { 11659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta doubleList[i] = bis.readDouble(); 11759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 11859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = doubleList; 11959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 12059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta default: 12159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new IllegalStateException("Invalid array subtype: " + subType); 12259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 12359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 12459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_GROUP: 12559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Structure group = (Structure) data.getFieldValue("group"); 12659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Structure> dataList = group.evaluateListBase(blenderContext); 12759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Properties> subProperties = new ArrayList<Properties>(len); 12859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Structure d : dataList) { 12959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Properties properties = new Properties(); 13059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta properties.load(d, blenderContext); 13159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta subProperties.add(properties); 13259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 13359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = subProperties; 13459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 13559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_DOUBLE: 13659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int doublePart1 = ((Number) data.getFieldValue("val")).intValue(); 13759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int doublePart2 = ((Number) data.getFieldValue("val2")).intValue(); 13859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta long doubleVal = (long) doublePart2 << 32 | doublePart1; 13959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = Double.valueOf(Double.longBitsToDouble(doubleVal)); 14059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 14159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_IDPARRAY: { 14259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Pointer pointer = (Pointer) data.getFieldValue("pointer"); 14359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Structure> arrays = pointer.fetchData(blenderContext.getInputStream()); 14459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Object> result = new ArrayList<Object>(arrays.size()); 14559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Properties temp = new Properties(); 14659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Structure array : arrays) { 14759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta temp.load(array, blenderContext); 14859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta result.add(temp.value); 14959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 15059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.value = result; 15159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 15259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 15359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_NUMTYPES: 15459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new UnsupportedOperationException(); 15559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // case IDP_ID://not yet implemented in blender 15659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // return null; 15759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta default: 15859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta throw new IllegalStateException("Unknown custom property type: " + type); 15959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.completeLoading(); 16159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 16259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 16359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 16459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method returns the name of the property. 16559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @return the name of the property 16659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 16759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public String getName() { 16859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return name; 16959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 17059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 17159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 17259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method returns the description of the property. 17359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @return the description of the property 17459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 17559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public String getDescription() { 17659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return description; 17759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 17859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 17959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 18059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method returns the type of the property. 18159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @return the type of the property 18259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 18359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public int getType() { 18459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return type; 18559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 18659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 18759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 18859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method returns the value of the property. 18959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * The type of the value depends on the type of the property. 19059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @return the value of the property 19159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 19259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Object getValue() { 19359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return value; 19459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 19559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 19659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 19759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method returns the same as getValue if the current property is of 19859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * other type than IDP_GROUP and its name matches 'propertyName' param. If 19959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * this property is a group property the method tries to find subproperty 20059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * value of the given name. The first found value is returnes os <b>use this 20159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * method wisely</b>. If no property of a given name is foung - <b>null</b> 20259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * is returned. 20359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * 20459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param propertyName 20559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * the name of the property 20659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @return found property value or <b>null</b> 20759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 20859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @SuppressWarnings("unchecked") 20959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public Object findValue(String propertyName) { 21059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (name.equals(propertyName)) { 21159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return value; 21259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 21359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (type == IDP_GROUP) { 21459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Properties> props = (List<Properties>) value; 21559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Properties p : props) { 21659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Object v = p.findValue(propertyName); 21759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (v != null) { 21859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return v; 21959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 22059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 22159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 22259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 22359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return null; 22459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 22559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 22659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @Override 22759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public String toString() { 22859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta StringBuilder sb = new StringBuilder(); 22959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta this.append(sb, new StringBuilder()); 23059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return sb.toString(); 23159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 23259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 23359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 23459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method appends the data of the property to the given string buffer. 23559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param sb 23659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * string buffer 23759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * @param indent 23859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * indent buffer 23959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 24059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @SuppressWarnings("unchecked") 24159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta private void append(StringBuilder sb, StringBuilder indent) { 24259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append(indent).append("name: ").append(name).append("\n\r"); 24359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append(indent).append("type: ").append(type).append("\n\r"); 24459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append(indent).append("subType: ").append(subType).append("\n\r"); 24559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append(indent).append("description: ").append(description).append("\n\r"); 24659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta indent.append('\t'); 24759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append(indent).append("value: "); 24859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (value instanceof Properties) { 24959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ((Properties) value).append(sb, indent); 25059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else if (value instanceof List) { 25159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Object v : (List<Object>) value) { 25259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (v instanceof Properties) { 25359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append(indent).append("{\n\r"); 25459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta indent.append('\t'); 25559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta ((Properties) v).append(sb, indent); 25659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta indent.deleteCharAt(indent.length() - 1); 25759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append(indent).append("}\n\r"); 25859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 25959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append(v); 26059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 26159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 26259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else { 26359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append(value); 26459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 26559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta sb.append("\n\r"); 26659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta indent.deleteCharAt(indent.length() - 1); 26759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 26859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 26959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta /** 27059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * This method should be called after the properties loading. 27159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * It loads the properties from the _RNA_UI property and removes this property from the 27259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta * result list. 27359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta */ 27459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @SuppressWarnings("unchecked") 27559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta protected void completeLoading() { 27659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (this.type == IDP_GROUP) { 27759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Properties> groupProperties = (List<Properties>) this.value; 27859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Properties rnaUI = null; 27959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Properties properties : groupProperties) { 28059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (properties.name.equals(RNA_PROPERTY_NAME) && properties.type == IDP_GROUP) { 28159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta rnaUI = properties; 28259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 28359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 28459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 28559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (rnaUI != null) { 28659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // removing the RNA from the result list 28759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta groupProperties.remove(rnaUI); 28859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 28959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // loading the descriptions 29059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Map<String, String> descriptions = new HashMap<String, String>(groupProperties.size()); 29159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Properties> propertiesRNA = (List<Properties>) rnaUI.value; 29259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Properties properties : propertiesRNA) { 29359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta String name = properties.name; 29459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta String description = null; 29559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta List<Properties> rnaData = (List<Properties>) properties.value; 29659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Properties rna : rnaData) { 29759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if ("description".equalsIgnoreCase(rna.name)) { 29859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta description = (String) rna.value; 29959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 30059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 30159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 30259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta descriptions.put(name, description); 30359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 30459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 30559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // applying the descriptions 30659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta for (Properties properties : groupProperties) { 30759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta properties.description = descriptions.get(properties.name); 30859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 30959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 31059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 31159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 31259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 31359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @Override 31459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @SuppressWarnings({ "rawtypes", "unchecked" }) 31559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void write(JmeExporter ex) throws IOException { 31659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta OutputCapsule oc = ex.getCapsule(this); 31759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write(name, "name", DEFAULT_NAME); 31859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write(type, "type", 0); 31959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write(subType, "subtype", 0); 32059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write(description, "description", null); 32159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta switch (type) { 32259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_STRING: 32359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write((String) value, "value", null); 32459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 32559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_INT: 32659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write((Integer) value, "value", 0); 32759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 32859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_FLOAT: 32959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write((Float) value, "value", 0); 33059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 33159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_ARRAY: 33259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta switch (subType) { 33359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_INT: 33459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write((int[]) value, "value", null); 33559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 33659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_FLOAT: 33759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write((float[]) value, "value", null); 33859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 33959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_DOUBLE: 34059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write((double[]) value, "value", null); 34159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 34259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta default: 34359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta LOGGER.warning("Cannot save the property's value! Invalid array subtype! Property: name: " + name + "; subtype: " + subType); 34459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 34559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_GROUP: 34659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.writeSavableArrayList((ArrayList<Properties>) value, "value", null); 34759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 34859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_DOUBLE: 34959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.write((Double) value, "value", 0); 35059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 35159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_IDPARRAY: 35259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta oc.writeSavableArrayList((ArrayList) value, "value", null); 35359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 35459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_NUMTYPES: 35559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta LOGGER.warning("Numtypes value not supported! Cannot write it!"); 35659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 35759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // case IDP_ID://not yet implemented in blender 35859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // break; 35959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta default: 36059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta LOGGER.warning("Cannot save the property's value! Invalid type! Property: name: " + name + "; type: " + type); 36159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 36259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 36359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 36459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @Override 36559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public void read(JmeImporter im) throws IOException { 36659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta InputCapsule ic = im.getCapsule(this); 36759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta name = ic.readString("name", DEFAULT_NAME); 36859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta type = ic.readInt("type", 0); 36959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta subType = ic.readInt("subtype", 0); 37059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta description = ic.readString("description", null); 37159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta switch (type) { 37259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_STRING: 37359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = ic.readString("value", null); 37459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 37559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_INT: 37659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = Integer.valueOf(ic.readInt("value", 0)); 37759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 37859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_FLOAT: 37959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = Float.valueOf(ic.readFloat("value", 0.0f)); 38059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 38159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_ARRAY: 38259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta switch (subType) { 38359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_INT: 38459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = ic.readIntArray("value", null); 38559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 38659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_FLOAT: 38759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = ic.readFloatArray("value", null); 38859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 38959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_DOUBLE: 39059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = ic.readDoubleArray("value", null); 39159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 39259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta default: 39359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta LOGGER.warning("Cannot read the property's value! Invalid array subtype! Property: name: " + name + "; subtype: " + subType); 39459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 39559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_GROUP: 39659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = ic.readSavable("value", null); 39759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 39859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_DOUBLE: 39959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = Double.valueOf(ic.readDouble("value", 0.0)); 40059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 40159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_IDPARRAY: 40259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta value = ic.readSavableArrayList("value", null); 40359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 40459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta case IDP_NUMTYPES: 40559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta LOGGER.warning("Numtypes value not supported! Cannot read it!"); 40659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta break; 40759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // case IDP_ID://not yet implemented in blender 40859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta // break; 40959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta default: 41059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta LOGGER.warning("Cannot read the property's value! Invalid type! Property: name: " + name + "; type: " + type); 41159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 41259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 41359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 41459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @Override 41559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public int hashCode() { 41659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta final int prime = 31; 41759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta int result = 1; 41859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta result = prime * result + (description == null ? 0 : description.hashCode()); 41959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta result = prime * result + (name == null ? 0 : name.hashCode()); 42059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta result = prime * result + subType; 42159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta result = prime * result + type; 42259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta result = prime * result + (value == null ? 0 : value.hashCode()); 42359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return result; 42459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 42559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta 42659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta @Override 42759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta public boolean equals(Object obj) { 42859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (this == obj) { 42959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return true; 43059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 43159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (obj == null) { 43259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 43359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 43459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (this.getClass() != obj.getClass()) { 43559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 43659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 43759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta Properties other = (Properties) obj; 43859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (description == null) { 43959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (other.description != null) { 44059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 44159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 44259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else if (!description.equals(other.description)) { 44359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 44459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 44559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (name == null) { 44659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (other.name != null) { 44759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 44859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 44959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else if (!name.equals(other.name)) { 45059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 45159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 45259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (subType != other.subType) { 45359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 45459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 45559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (type != other.type) { 45659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 45759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 45859b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (value == null) { 45959b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta if (other.value != null) { 46059b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 46159b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 46259b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } else if (!value.equals(other.value)) { 46359b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return false; 46459b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 46559b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta return true; 46659b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta } 46759b2e6871c65f58fdad78cd7229c292f6a177578Scott Barta} 468