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 }