1 /* 2 * Entreri, an entity-component framework in Java 3 * 4 * Copyright (c) 2013, Michael Ludwig 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without modification, 8 * are permitted provided that the following conditions are met: 9 * 10 * Redistributions of source code must retain the above copyright notice, 11 * this list of conditions and the following disclaimer. 12 * Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 20 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 23 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 package com.lhkbob.entreri.property; 28 29 import java.lang.reflect.Method; 30 import java.util.Arrays; 31 32 /** 33 * ObjectProperty is an implementation of Property that stores the property data as a 34 * number of packed Object references for each property. Because it is not primitive data, 35 * cache locality will suffer compared to the primitive property types, but it will allow 36 * you to store arbitrary objects. 37 * <p/> 38 * However, ObjectProperty assumes that all component values can be null, and the default 39 * value is null. If this is not an acceptable contract then a custom property must be 40 * defined with a factory capable of constructing proper default instances. 41 * 42 * @author Michael Ludwig 43 */ 44 @Factory(ObjectProperty.Factory.class) 45 public final class ObjectProperty implements Property { 46 private Object[] data; 47 48 /** 49 * Create an ObjectProperty. 50 */ 51 public ObjectProperty() { 52 data = new Object[1]; 53 } 54 55 /** 56 * Return a PropertyFactory that creates ObjectProperties using the given cloning 57 * policy. 58 * 59 * @return A PropertyFactory for ObjectProperty 60 */ 61 public static PropertyFactory<ObjectProperty> factory(Clone.Policy policy) { 62 return new Factory(policy); 63 } 64 65 /** 66 * Return the backing int array of this property's IndexedDataStore. The array may be 67 * longer than necessary for the number of components in the system. Data can be 68 * accessed for a component directly using the component's index. 69 * 70 * @return The Object data for all packed properties that this property has been 71 * packed with 72 */ 73 public Object[] getIndexedData() { 74 return data; 75 } 76 77 /** 78 * Get the value stored in this property for the given component index. 79 * 80 * @param componentIndex The component's index 81 * 82 * @return The object at the given offset for the given component 83 * 84 * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid 85 */ 86 @SuppressWarnings("unchecked") 87 public Object get(int componentIndex) { 88 return data[componentIndex]; 89 } 90 91 /** 92 * Store <var>val</var> in this property for the given component index. 93 * 94 * @param componentIndex The index of the component being modified 95 * @param val The value to store, can be null 96 * 97 * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid 98 */ 99 public void set(int componentIndex, Object val) { 100 data[componentIndex] = val; 101 } 102 103 @Override 104 public void swap(int a, int b) { 105 Object t = data[a]; 106 data[a] = data[b]; 107 data[b] = t; 108 } 109 110 @Override 111 public int getCapacity() { 112 return data.length; 113 } 114 115 @Override 116 public void setCapacity(int size) { 117 data = Arrays.copyOf(data, size); 118 } 119 120 /** 121 * Factory to create ObjectProperties. 122 * 123 * @author Michael Ludwig 124 */ 125 @SuppressWarnings({ "rawtypes", "unchecked" }) 126 public static class Factory implements PropertyFactory<ObjectProperty> { 127 private final Clone.Policy policy; 128 129 public Factory(Attributes attrs) { 130 policy = attrs.hasAttribute(Clone.class) ? attrs.getAttribute(Clone.class) 131 .value() 132 : Clone.Policy.JAVA_DEFAULT; 133 } 134 135 public Factory(Clone.Policy policy) { 136 this.policy = policy; 137 } 138 139 @Override 140 public ObjectProperty create() { 141 return new ObjectProperty(); 142 } 143 144 @Override 145 public void setDefaultValue(ObjectProperty property, int index) { 146 property.set(index, null); 147 } 148 149 @Override 150 public void clone(ObjectProperty src, int srcIndex, ObjectProperty dst, 151 int dstIndex) { 152 switch (policy) { 153 case DISABLE: 154 // assign default value 155 setDefaultValue(dst, dstIndex); 156 break; 157 case INVOKE_CLONE: 158 Object orig = src.get(srcIndex); 159 if (orig instanceof Cloneable) { 160 try { 161 // if they implemented Cloneable properly, clone() should 162 // be public and take no arguments 163 Method cloneMethod = orig.getClass().getMethod("clone"); 164 Object cloned = cloneMethod.invoke(orig); 165 dst.set(dstIndex, cloned); 166 break; 167 } catch (Exception e) { 168 // if they implement Cloneable, this shouldn't fail 169 // and if it does it's not really our fault 170 throw new RuntimeException(e); 171 } 172 } 173 // else fall through to java default 174 case JAVA_DEFAULT: 175 dst.set(dstIndex, src.get(srcIndex)); 176 break; 177 default: 178 throw new UnsupportedOperationException( 179 "Enum value not supported: " + policy); 180 } 181 } 182 } 183 }