Commits

Michael Ludwig committed ce0fbdf

Remove IndexedDataStore and consolidate it into the Property implementations.

Comments (0)

Files changed (30)

src/main/java/com/lhkbob/entreri/EntitySystem.java

         return new EntitySystemImpl();
     }
 
-
-    /**
-     * Estimate the memory usage of the components with the given TypeId in this
-     * EntitySystem. The returned long is measured in bytes. For Component types that
-     * store primitive data, the estimate will be quite accurate. For types that store
-     * references to objects, it will likely be an underestimate.
-     *
-     * @param type The component type whose memory usage is estimated
-     *
-     * @return The memory estimate for the given type
-     *
-     * @throws NullPointerException if id is null
-     */
-    public abstract long estimateMemory(Class<? extends Component> type);
-
     /**
      * Get all Component types within this EntitySystem that have types assignable to the
      * input <var>type</var>.

src/main/java/com/lhkbob/entreri/impl/ComponentRepository.java

 import com.lhkbob.entreri.Component;
 import com.lhkbob.entreri.Entity;
 import com.lhkbob.entreri.Requires;
-import com.lhkbob.entreri.property.*;
+import com.lhkbob.entreri.property.IntProperty;
+import com.lhkbob.entreri.property.ObjectProperty;
+import com.lhkbob.entreri.property.Property;
+import com.lhkbob.entreri.property.PropertyFactory;
 
 import java.lang.ref.WeakReference;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
 
 /**
  * ComponentRepository manages storing all the componentDatas of a specific type for an
     }
 
     /**
-     * @return Estimated memory usage of this component repository
-     */
-    public long estimateMemory() {
-        long total =
-                estimateMemory(declaredProperties) + estimateMemory(decoratedProperties);
-
-        // also add in an estimate for other structures used by
-        // this repository
-        total += 4 * entityIndexToComponentRepository.length;
-        total += 4 * componentIndexToEntityIndex.length;
-
-        // estimate each Component object as 4 bytes for a pointer,
-        // 4 bytes for its index, 4 bytes for its repository reference
-        total += 12 * components.length;
-
-        return total;
-    }
-
-    private long estimateMemory(List<? extends PropertyStore<?>> properties) {
-        long total = 0L;
-        int ct = properties.size();
-        for (int i = 0; i < ct; i++) {
-            Property p = properties.get(i).getProperty();
-            if (p != null) {
-                total += p.getDataStore().memory();
-            }
-        }
-        return total;
-    }
-
-    /**
      * @param componentIndex The component index
      *
      * @return The component id of the component at the given index
         return oldComponent != null;
     }
 
-    /*
-     * Update all component data in the list of properties. If possible the data
-     * store in swap is reused.
-     */
-    private void update(List<? extends PropertyStore<?>> properties) {
-        for (int i = 0; i < properties.size(); i++) {
-            PropertyStore<?> store = properties.get(i);
-            Property p = store.getProperty();
-            if (p != null) {
-                IndexedDataStore origStore = p.getDataStore();
-                p.setDataStore(update(origStore, store.swap));
-                store.swap = origStore;
+    private void sort() {
+        // perform an insertion sort, since most components are likely to be
+        // ordered correctly the performance will be almost linear
+
+        for (int i = 1; i < componentInsert; i++) {
+            int vi = componentIndexToEntityIndex[i];
+            for (int j = i - 1; j >= 1; j--) {
+                int vj = componentIndexToEntityIndex[j];
+
+                // move an index left if it is valid and it's less than
+                // the prior index or if the prior index is invalid
+                if (vi > 0 && (vi < vj || vj == 0)) {
+                    // must swap in place
+                    componentIndexToEntityIndex[j] = vi;
+                    componentIndexToEntityIndex[j + 1] = vj;
+
+                    T t = components[j];
+                    components[j] = components[j + 1];
+                    components[j + 1] = t;
+
+                    // keep property data inline with components
+                    swap(declaredProperties, j + 1, j);
+                    swap(decoratedProperties, j + 1, j);
+                } else {
+                    // reached proper point in sorted sublist
+                    break;
+                }
             }
         }
     }
 
-    /*
-     * Update all component data in src to be in dst by shuffling it to match
-     * current state of components array.
-     */
-    private IndexedDataStore update(IndexedDataStore src, IndexedDataStore dst) {
-        int dstSize = components.length;
-
-        if (dst == null || dst.size() < dstSize) {
-            dst = src.create(dstSize);
+    private void swap(List<? extends PropertyStore<?>> store, int a, int b) {
+        for (int i = 0; i < store.size(); i++) {
+            store.get(i).swap(a, b);
         }
-
-        int i;
-        int lastIndex = -1;
-        int copyIndexNew = -1;
-        int copyIndexOld = -1;
-        for (i = 1; i < componentInsert; i++) {
-            if (components[i] == null) {
-                // we've hit the end of existing componentDatas, so break
-                break;
-            }
-
-            if (components[i].getIndex() != lastIndex + 1) {
-                // we are not in a contiguous section
-                if (copyIndexOld >= 0) {
-                    // we have to copy over the last section
-                    src.copy(copyIndexOld, (i - copyIndexNew), dst, copyIndexNew);
-                }
-
-                // set the copy indices
-                copyIndexNew = i;
-                copyIndexOld = components[i].getIndex();
-            }
-            lastIndex = components[i].getIndex();
-        }
-
-        if (copyIndexOld >= 0) {
-            // final copy
-            src.copy(copyIndexOld, (i - copyIndexNew), dst, copyIndexNew);
-        }
-
-        return dst;
     }
 
     /**
      * @param numEntities       The number of entities that are in the system
      */
     public void compact(int[] entityOldToNewMap, int numEntities) {
-        // First sort the canonical components array to order them by their entity
-        Arrays.sort(components, 1, componentInsert, new Comparator<T>() {
-            @Override
-            public int compare(T o1, T o2) {
-                if (o1 != null && o2 != null) {
-                    return componentIndexToEntityIndex[o1.getIndex()] -
-                           componentIndexToEntityIndex[o2.getIndex()];
-                } else if (o1 != null) {
-                    return -1; // push null o2 to end of array
-                } else if (o2 != null) {
-                    return 1; // push null o1 to end of array
-                } else {
-                    return 0; // both null so they are "equal"
-                }
-            }
-        });
-
         // Remove all WeakPropertyStores that no longer have a property
         Iterator<DecoratedPropertyStore<?>> it = decoratedProperties.iterator();
         while (it.hasNext()) {
             }
         }
 
-        // Update all of the property stores to match up with the components new positions
-        update(declaredProperties);
-        update(decoratedProperties);
+        // Sort the canonical components array to order them by their entity, which
+        // also keeps the property data valid
+        sort();
 
         // Repair the componentToEntityIndex and the component.index values
         componentInsert = 1;
-        int[] newComponentRepository = new int[components.length];
         for (int i = 1; i < components.length; i++) {
             if (components[i] != null) {
-                newComponentRepository[i] = entityOldToNewMap[componentIndexToEntityIndex[components[i]
-                        .getIndex()]];
+                componentIndexToEntityIndex[i] = entityOldToNewMap[componentIndexToEntityIndex[i]];
                 ((AbstractComponent<T>) components[i]).setIndex(i);
                 componentInsert = i + 1;
+            } else {
+                // we can terminate now since all future components should be null
+                // since we've sorted it that way
+                break;
             }
         }
-        componentIndexToEntityIndex = newComponentRepository;
 
         // Possibly compact the component data
         if (componentInsert < .6 * components.length) {
     public <P extends Property> P decorate(PropertyFactory<P> factory) {
         int size = (declaredProperties.isEmpty() ? componentInsert
                                                  : declaredProperties.get(0).property
-                            .getDataStore().size());
+                            .getCapacity());
         P prop = factory.create();
         DecoratedPropertyStore<P> pstore = new DecoratedPropertyStore<P>(factory, prop);
 
         // Set values from factory to all component slots
-        IndexedDataStore newStore = prop.getDataStore().create(size);
-        prop.setDataStore(newStore);
+        prop.setCapacity(size);
         for (int i = 1; i < size; i++) {
             pstore.setDefaultValue(i);
         }
      */
     private static abstract class PropertyStore<P extends Property> {
         final PropertyFactory<P> creator;
-        IndexedDataStore swap; // may be null;
 
         PropertyStore(PropertyFactory<P> creator) {
             this.creator = creator;
-            swap = null;
         }
 
         void setDefaultValue(int index) {
         void resize(int size) {
             P property = getProperty();
             if (property != null) {
-                IndexedDataStore oldStore = property.getDataStore();
-                IndexedDataStore newStore = oldStore.create(size);
-                oldStore.copy(0, Math.min(oldStore.size(), size), newStore, 0);
-                property.setDataStore(newStore);
+                property.setCapacity(size);
+            }
+        }
+
+        void swap(int a, int b) {
+            P property = getProperty();
+            if (property != null) {
+                property.swap(a, b);
             }
         }
 

src/main/java/com/lhkbob/entreri/impl/EntitySystemImpl.java

     }
 
     @Override
-    public long estimateMemory(Class<? extends Component> type) {
-        int index = getTypeIndex(type);
-        if (index < componentRepositories.length) {
-            ComponentRepository<?> repo = componentRepositories[index];
-            return repo.estimateMemory();
-        } else {
-            return 0L;
-        }
-    }
-
-    @Override
     @SuppressWarnings({ "rawtypes", "unchecked" })
     public <T extends Component> Collection<Class<? extends T>> getComponentTypes(
             Class<T> type) {

src/main/java/com/lhkbob/entreri/property/AbstractIndexedDataStore.java

-/*
- * Entreri, an entity-component framework in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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.lhkbob.entreri.property;
-
-import java.nio.Buffer;
-import java.util.List;
-
-/**
- * <p/>
- * AbstractIndexedDataStore is an implementation of IndexedDataStore that uses an array to
- * hold the packed property values of the store. It implements the vast majority of the
- * logic needed for an IndexedDataStore, and concrete classes are only required to create
- * and store the arrays.
- * <p/>
- * An AbstractIndexedDataStore instance will only have one array in its lifetime.
- *
- * @author Michael Ludwig
- */
-public abstract class AbstractIndexedDataStore implements IndexedDataStore {
-    protected final int elementSize;
-
-    /**
-     * Create an AbstractIndexedDataStore that will use <var>elementSize</var> array
-     * elements per ComponentData in the data store. This does not create a backing array,
-     * so concrete classes must allocate an initial array.
-     *
-     * @param elementSize The number of array elements per property instance
-     *
-     * @throws IllegalArgumentException if elementSize is less than 1
-     */
-    public AbstractIndexedDataStore(int elementSize) {
-        if (elementSize < 1) {
-            throw new IllegalArgumentException("Element size must be at least 1");
-        }
-        this.elementSize = elementSize;
-    }
-
-    /**
-     * @return The number of primitive elements per instance
-     */
-    public int getElementSize() {
-        return elementSize;
-    }
-
-    @Override
-    public int size() {
-        return getArrayLength() / elementSize;
-    }
-
-    @Override
-    public void copy(int srcOffset, int len, IndexedDataStore dest, int destOffset) {
-        if (dest == null) {
-            throw new NullPointerException("Destination store cannot be null");
-        }
-        if (!(getClass().isInstance(dest))) {
-            throw new IllegalArgumentException(
-                    "Destination store not compatible with this store, wrong type: " +
-                    dest.getClass());
-        }
-
-        AbstractIndexedDataStore dstStore = (AbstractIndexedDataStore) dest;
-        if (dstStore.elementSize != elementSize) {
-            throw new IllegalArgumentException(
-                    "Destination store not compatible with this store, wrong element size: " +
-                    dstStore.elementSize);
-        }
-
-        arraycopy(getArray(), srcOffset * elementSize, dstStore.getArray(),
-                  destOffset * elementSize, len * elementSize);
-    }
-
-    /**
-     * <p/>
-     * Copy <var>len</var> elements of <var>oldArray</var> starting at
-     * <var>srcOffset</var> into <var>newArray</var> at <var>dstOffset</var>. The default
-     * implementation uses {@link System#arraycopy(Object, int, Object, int, int)}, which
-     * is suitable unless the backing data types are not primitive Java arrays.
-     * <p/>
-     * This can be overridden if the backing data is some other type, such as a {@link
-     * List} or {@link Buffer}, in which case the "array copy" can be simulated in this
-     * method.
-     *
-     * @param oldArray  The source array
-     * @param srcOffset The element offset into the source array
-     * @param newArray  The destination array where data is copied
-     * @param dstOffset The element offset into the new array
-     * @param len       The number of array elements to copy
-     */
-    protected void arraycopy(Object oldArray, int srcOffset, Object newArray,
-                             int dstOffset, int len) {
-        System.arraycopy(oldArray, srcOffset, newArray, dstOffset, len);
-    }
-
-    /**
-     * @return The array instance storing property data
-     */
-    protected abstract Object getArray();
-
-    /**
-     * @return The length of the array backing this data store
-     */
-    protected abstract int getArrayLength();
-}

src/main/java/com/lhkbob/entreri/property/AbstractPropertyFactory.java

-/*
- * Entreri, an entity-component framework in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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.lhkbob.entreri.property;
-
-/**
- * AbstractPropertyFactory is an abstract PropertyFactory implementation that implements
- * {@link #clone(Property, int, Property, int)} in terms of {@link
- * IndexedDataStore#copy(int, int, IndexedDataStore, int)}. In many cases this is
- * sufficient for most PropertyFactories.
- *
- * @param <P>
- *
- * @author Michael Ludwig
- */
-public abstract class AbstractPropertyFactory<P extends Property>
-        implements PropertyFactory<P> {
-    protected final Attributes attributes;
-    protected final Clone.Policy clonePolicy;
-
-    public AbstractPropertyFactory(Attributes attrs) {
-        attributes = attrs;
-
-        if (attrs != null && attrs.hasAttribute(Clone.class)) {
-            clonePolicy = attrs.getAttribute(Clone.class).value();
-        } else {
-            clonePolicy = Clone.Policy.JAVA_DEFAULT;
-        }
-    }
-
-    @Override
-    public void clone(P src, int srcIndex, P dst, int dstIndex) {
-        switch (clonePolicy) {
-        case DISABLE:
-            // assign default value
-            setDefaultValue(dst, dstIndex);
-            break;
-        case INVOKE_CLONE:
-            // fall through, since default implementation of INVOKE_CLONE is to
-            // just function like JAVA_DEFAULT
-        case JAVA_DEFAULT:
-            src.getDataStore().copy(srcIndex, 1, dst.getDataStore(), dstIndex);
-            break;
-        default:
-            throw new UnsupportedOperationException(
-                    "Enum value not supported: " + clonePolicy);
-        }
-    }
-}

src/main/java/com/lhkbob/entreri/property/BooleanDataStore.java

-/*
- * Entreri, an entity-component framework in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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.lhkbob.entreri.property;
-
-/**
- * BooleanDataStore is an IndexedDataStore that uses boolean arrays to store multi-element
- * component data.
- *
- * @author Michael Ludwig
- */
-public class BooleanDataStore extends AbstractIndexedDataStore {
-    private final boolean[] array;
-
-    /**
-     * Create a new BooleanDataStore with the given number of elements per logical
-     * component, and backed by the given array. The array's length must be a multiple of
-     * element size.
-     *
-     * @param elementSize The number of elements per component
-     * @param array       Backing array
-     *
-     * @throws IllegalArgumentException if array length is not a multiple of element size
-     */
-    public BooleanDataStore(int elementSize, boolean[] array) {
-        super(elementSize);
-        if (array.length % elementSize != 0) {
-            throw new IllegalArgumentException(
-                    "Array length must be a multiple of the element size");
-        }
-        this.array = array;
-    }
-
-    @Override
-    public long memory() {
-        return array.length;
-    }
-
-    @Override
-    public BooleanDataStore create(int size) {
-        return new BooleanDataStore(elementSize, new boolean[elementSize * size]);
-    }
-
-    @Override
-    public boolean[] getArray() {
-        return array;
-    }
-
-    @Override
-    protected int getArrayLength() {
-        return array.length;
-    }
-}

src/main/java/com/lhkbob/entreri/property/BooleanProperty.java

 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * BooleanProperty is an implementation of Property that stores a single boolean
  */
 @Factory(BooleanProperty.Factory.class)
 public final class BooleanProperty implements Property {
-    private BooleanDataStore store;
+    private boolean[] data;
 
     /**
      * Create a BooleanProperty.
      */
     public BooleanProperty() {
-        store = new BooleanDataStore(1, new boolean[1]);
+        data = new boolean[1];
     }
 
     /**
      *         packed with
      */
     public boolean[] getIndexedData() {
-        return store.getArray();
+        return data;
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public boolean get(int componentIndex) {
-        return store.getArray()[componentIndex];
+        return data[componentIndex];
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public void set(boolean val, int componentIndex) {
-        store.getArray()[componentIndex] = val;
+        data[componentIndex] = val;
     }
 
     @Override
-    public IndexedDataStore getDataStore() {
-        return store;
+    public void swap(int a, int b) {
+        boolean t = data[a];
+        data[a] = data[b];
+        data[b] = t;
     }
 
     @Override
-    public void setDataStore(IndexedDataStore store) {
-        if (store == null) {
-            throw new NullPointerException("Store cannot be null");
-        }
-        if (!(store instanceof BooleanDataStore)) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with FloatProperty, wrong type: " +
-                    store.getClass());
-        }
+    public int getCapacity() {
+        return data.length;
+    }
 
-        BooleanDataStore newStore = (BooleanDataStore) store;
-        if (newStore.elementSize != this.store.elementSize) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with FloatProperty, wrong element size: " +
-                    newStore.elementSize);
-        }
-
-        this.store = newStore;
+    @Override
+    public void setCapacity(int size) {
+        data = Arrays.copyOf(data, size);
     }
 
     /**
      *
      * @author Michael Ludwig
      */
-    public static class Factory extends AbstractPropertyFactory<BooleanProperty> {
+    public static class Factory implements PropertyFactory<BooleanProperty> {
         private final boolean defaultValue;
+        private final Clone.Policy policy;
 
         public Factory(Attributes attrs) {
-            super(attrs);
-
-            if (attrs.hasAttribute(DefaultBoolean.class)) {
-                defaultValue = attrs.getAttribute(DefaultBoolean.class).value();
-            } else {
-                defaultValue = false;
-            }
+            defaultValue = attrs.hasAttribute(DefaultBoolean.class) &&
+                           attrs.getAttribute(DefaultBoolean.class).value();
+            policy = attrs.hasAttribute(Clone.class) ? attrs.getAttribute(Clone.class)
+                                                            .value()
+                                                     : Clone.Policy.JAVA_DEFAULT;
         }
 
         public Factory(boolean defaultValue) {
-            super(null);
             this.defaultValue = defaultValue;
+            policy = Clone.Policy.JAVA_DEFAULT;
         }
 
         @Override
         public void setDefaultValue(BooleanProperty property, int index) {
             property.set(defaultValue, index);
         }
+
+        @Override
+        public void clone(BooleanProperty src, int srcIndex, BooleanProperty dst,
+                          int dstIndex) {
+            switch (policy) {
+            case DISABLE:
+                // assign default value
+                setDefaultValue(dst, dstIndex);
+                break;
+            case INVOKE_CLONE:
+                // fall through, since default implementation of INVOKE_CLONE is to
+                // just function like JAVA_DEFAULT
+            case JAVA_DEFAULT:
+                dst.set(src.get(srcIndex), dstIndex);
+                break;
+            default:
+                throw new UnsupportedOperationException(
+                        "Enum value not supported: " + policy);
+            }
+        }
     }
 
     /**

src/main/java/com/lhkbob/entreri/property/ByteDataStore.java

-/*
- * Entreri, an entity-component framework in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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.lhkbob.entreri.property;
-
-/**
- * ByteDataStore is an IndexedDataStore that uses byte arrays to store multi-element
- * component data.
- *
- * @author Michael Ludwig
- */
-public class ByteDataStore extends AbstractIndexedDataStore {
-    private final byte[] array;
-
-    /**
-     * Create a new ByteDataStore with the given number of elements per logical component,
-     * and backed by the given array. The array's length must be a multiple of element
-     * size.
-     *
-     * @param elementSize The number of elements per component
-     * @param array       Backing array
-     *
-     * @throws IllegalArgumentException if array length is not a multiple of element size
-     */
-    public ByteDataStore(int elementSize, byte[] array) {
-        super(elementSize);
-        this.array = array;
-    }
-
-    @Override
-    public long memory() {
-        return array.length;
-    }
-
-    @Override
-    public ByteDataStore create(int size) {
-        return new ByteDataStore(elementSize, new byte[elementSize * size]);
-    }
-
-    @Override
-    public byte[] getArray() {
-        return array;
-    }
-
-    @Override
-    protected int getArrayLength() {
-        return array.length;
-    }
-}

src/main/java/com/lhkbob/entreri/property/ByteProperty.java

 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * ByteProperty is an implementation of Property that stores a single byte value.
  */
 @Factory(ByteProperty.Factory.class)
 public final class ByteProperty implements Property {
-    private ByteDataStore store;
+    private byte[] data;
 
     /**
      * Create an ByteProperty.
      */
     public ByteProperty() {
-        store = new ByteDataStore(1, new byte[1]);
+        data = new byte[1];
     }
 
     /**
      *         with
      */
     public byte[] getIndexedData() {
-        return store.getArray();
+        return data;
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public byte get(int componentIndex) {
-        return store.getArray()[componentIndex];
+        return data[componentIndex];
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public void set(byte val, int componentIndex) {
-        store.getArray()[componentIndex] = val;
+        data[componentIndex] = val;
     }
 
     @Override
-    public IndexedDataStore getDataStore() {
-        return store;
+    public void swap(int a, int b) {
+        byte t = data[a];
+        data[a] = data[b];
+        data[b] = t;
     }
 
     @Override
-    public void setDataStore(IndexedDataStore store) {
-        if (store == null) {
-            throw new NullPointerException("Store cannot be null");
-        }
-        if (!(store instanceof ByteDataStore)) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with ByteProperty, wrong type: " +
-                    store.getClass());
-        }
+    public int getCapacity() {
+        return data.length;
+    }
 
-        ByteDataStore newStore = (ByteDataStore) store;
-        if (newStore.elementSize != this.store.elementSize) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with ByteProperty, wrong element size: " +
-                    newStore.elementSize);
-        }
-
-        this.store = newStore;
+    @Override
+    public void setCapacity(int size) {
+        data = Arrays.copyOf(data, size);
     }
 
     /**
      *
      * @author Michael Ludwig
      */
-    public static class Factory extends AbstractPropertyFactory<ByteProperty> {
+    public static class Factory implements PropertyFactory<ByteProperty> {
         private final byte defaultValue;
+        private final Clone.Policy policy;
 
         public Factory(Attributes attrs) {
-            super(attrs);
-
-            if (attrs.hasAttribute(DefaultByte.class)) {
-                defaultValue = attrs.getAttribute(DefaultByte.class).value();
-            } else {
-                defaultValue = 0;
-            }
+            defaultValue = attrs.hasAttribute(DefaultByte.class) ? attrs
+                    .getAttribute(DefaultByte.class).value() : 0;
+            policy = attrs.hasAttribute(Clone.class) ? attrs.getAttribute(Clone.class)
+                                                            .value()
+                                                     : Clone.Policy.JAVA_DEFAULT;
         }
 
         public Factory(byte defaultValue) {
-            super(null);
             this.defaultValue = defaultValue;
+            policy = Clone.Policy.JAVA_DEFAULT;
         }
 
         @Override
         public void setDefaultValue(ByteProperty property, int index) {
             property.set(defaultValue, index);
         }
+
+        @Override
+        public void clone(ByteProperty src, int srcIndex, ByteProperty dst,
+                          int dstIndex) {
+            switch (policy) {
+            case DISABLE:
+                // assign default value
+                setDefaultValue(dst, dstIndex);
+                break;
+            case INVOKE_CLONE:
+                // fall through, since default implementation of INVOKE_CLONE is to
+                // just function like JAVA_DEFAULT
+            case JAVA_DEFAULT:
+                dst.set(src.get(srcIndex), dstIndex);
+                break;
+            default:
+                throw new UnsupportedOperationException(
+                        "Enum value not supported: " + policy);
+            }
+        }
     }
 
     /**

src/main/java/com/lhkbob/entreri/property/CharDataStore.java

-/*
- * Entreri, an entity-component framework in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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.lhkbob.entreri.property;
-
-/**
- * CharDataStore is an IndexedDataStore that uses char arrays to store multi-element
- * component data.
- *
- * @author Michael Ludwig
- */
-public class CharDataStore extends AbstractIndexedDataStore {
-    private final char[] array;
-
-    /**
-     * Create a new CharDataStore with the given number of elements per logical component,
-     * and backed by the given array. The array's length must be a multiple of element
-     * size.
-     *
-     * @param elementSize The number of elements per component
-     * @param array       Backing array
-     *
-     * @throws IllegalArgumentException if array length is not a multiple of element size
-     */
-    public CharDataStore(int elementSize, char[] array) {
-        super(elementSize);
-        this.array = array;
-    }
-
-    @Override
-    public long memory() {
-        return 2 * array.length;
-    }
-
-    @Override
-    public CharDataStore create(int size) {
-        return new CharDataStore(elementSize, new char[elementSize * size]);
-    }
-
-    @Override
-    public char[] getArray() {
-        return array;
-    }
-
-    @Override
-    protected int getArrayLength() {
-        return array.length;
-    }
-}

src/main/java/com/lhkbob/entreri/property/CharProperty.java

 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * CharProperty is an implementation of Property that stores a single char value.
  */
 @Factory(CharProperty.Factory.class)
 public final class CharProperty implements Property {
-    private CharDataStore store;
+    private char[] data;
 
     /**
      * Create an CharProperty.
      */
     public CharProperty() {
-        store = new CharDataStore(1, new char[1]);
+        data = new char[1];
     }
 
     /**
      *         with
      */
     public char[] getIndexedData() {
-        return store.getArray();
+        return data;
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public char get(int componentIndex) {
-        return store.getArray()[componentIndex];
+        return data[componentIndex];
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public void set(char val, int componentIndex) {
-        store.getArray()[componentIndex] = val;
+        data[componentIndex] = val;
     }
 
     @Override
-    public IndexedDataStore getDataStore() {
-        return store;
+    public void swap(int a, int b) {
+        char t = data[a];
+        data[a] = data[b];
+        data[b] = t;
     }
 
     @Override
-    public void setDataStore(IndexedDataStore store) {
-        if (store == null) {
-            throw new NullPointerException("Store cannot be null");
-        }
-        if (!(store instanceof CharDataStore)) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with CharProperty, wrong type: " +
-                    store.getClass());
-        }
+    public int getCapacity() {
+        return data.length;
+    }
 
-        CharDataStore newStore = (CharDataStore) store;
-        if (newStore.elementSize != this.store.elementSize) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with CharProperty, wrong element size: " +
-                    newStore.elementSize);
-        }
-
-        this.store = newStore;
+    @Override
+    public void setCapacity(int size) {
+        data = Arrays.copyOf(data, size);
     }
 
     /**
      *
      * @author Michael Ludwig
      */
-    public static class Factory extends AbstractPropertyFactory<CharProperty> {
+    public static class Factory implements PropertyFactory<CharProperty> {
         private final char defaultValue;
+        private final Clone.Policy policy;
 
         public Factory(Attributes attrs) {
-            super(attrs);
-
-            if (attrs.hasAttribute(DefaultChar.class)) {
-                defaultValue = attrs.getAttribute(DefaultChar.class).value();
-            } else {
-                defaultValue = '\0';
-            }
+            defaultValue = attrs.hasAttribute(DefaultChar.class) ? attrs
+                    .getAttribute(DefaultChar.class).value() : '\0';
+            policy = attrs.hasAttribute(Clone.class) ? attrs.getAttribute(Clone.class)
+                                                            .value()
+                                                     : Clone.Policy.JAVA_DEFAULT;
         }
 
         public Factory(char defaultValue) {
-            super(null);
             this.defaultValue = defaultValue;
+            policy = Clone.Policy.JAVA_DEFAULT;
         }
 
         @Override
         public void setDefaultValue(CharProperty property, int index) {
             property.set(defaultValue, index);
         }
+
+        @Override
+        public void clone(CharProperty src, int srcIndex, CharProperty dst,
+                          int dstIndex) {
+            switch (policy) {
+            case DISABLE:
+                // assign default value
+                setDefaultValue(dst, dstIndex);
+                break;
+            case INVOKE_CLONE:
+                // fall through, since default implementation of INVOKE_CLONE is to
+                // just function like JAVA_DEFAULT
+            case JAVA_DEFAULT:
+                dst.set(src.get(srcIndex), dstIndex);
+                break;
+            default:
+                throw new UnsupportedOperationException(
+                        "Enum value not supported: " + policy);
+            }
+        }
     }
 
     /**

src/main/java/com/lhkbob/entreri/property/DoubleDataStore.java

-/*
- * Entreri, an entity-component framework in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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.lhkbob.entreri.property;
-
-/**
- * DoubleDataStore is an IndexedDataStore that uses double arrays to store multi-element
- * component data.
- *
- * @author Michael Ludwig
- */
-public class DoubleDataStore extends AbstractIndexedDataStore {
-    private final double[] array;
-
-    /**
-     * Create a new DoubleDataStore with the given number of elements per logical
-     * component, and backed by the given array. The array's length must be a multiple of
-     * element size.
-     *
-     * @param elementSize The number of elements per component
-     * @param array       Backing array
-     *
-     * @throws IllegalArgumentException if array length is not a multiple of element size
-     */
-    public DoubleDataStore(int elementSize, double[] array) {
-        super(elementSize);
-        this.array = array;
-    }
-
-    @Override
-    public long memory() {
-        return 8 * array.length;
-    }
-
-    @Override
-    public DoubleDataStore create(int size) {
-        return new DoubleDataStore(elementSize, new double[elementSize * size]);
-    }
-
-    @Override
-    public double[] getArray() {
-        return array;
-    }
-
-    @Override
-    protected int getArrayLength() {
-        return array.length;
-    }
-}

src/main/java/com/lhkbob/entreri/property/DoubleProperty.java

 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * DoubleProperty is an implementation of Property that stores a single double value.
  */
 @Factory(DoubleProperty.Factory.class)
 public final class DoubleProperty implements Property {
-    private DoubleDataStore store;
+    private double[] data;
 
     /**
      * Create an DoubleProperty.
      */
     public DoubleProperty() {
-        store = new DoubleDataStore(1, new double[1]);
+        data = new double[1];
     }
 
     /**
      *         packed with
      */
     public double[] getIndexedData() {
-        return store.getArray();
+        return data;
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public double get(int componentIndex) {
-        return store.getArray()[componentIndex];
+        return data[componentIndex];
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public void set(double val, int componentIndex) {
-        store.getArray()[componentIndex] = val;
+        data[componentIndex] = val;
     }
 
     @Override
-    public IndexedDataStore getDataStore() {
-        return store;
+    public void swap(int a, int b) {
+        double t = data[a];
+        data[a] = data[b];
+        data[b] = t;
     }
 
     @Override
-    public void setDataStore(IndexedDataStore store) {
-        if (store == null) {
-            throw new NullPointerException("Store cannot be null");
-        }
-        if (!(store instanceof DoubleDataStore)) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with DoubleProperty, wrong type: " +
-                    store.getClass());
-        }
+    public int getCapacity() {
+        return data.length;
+    }
 
-        DoubleDataStore newStore = (DoubleDataStore) store;
-        if (newStore.elementSize != this.store.elementSize) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with DoubleProperty, wrong element size: " +
-                    newStore.elementSize);
-        }
-
-        this.store = newStore;
+    @Override
+    public void setCapacity(int size) {
+        data = Arrays.copyOf(data, size);
     }
 
     /**
      *
      * @author Michael Ludwig
      */
-    public static class Factory extends AbstractPropertyFactory<DoubleProperty> {
+    public static class Factory implements PropertyFactory<DoubleProperty> {
         private final double defaultValue;
+        private final Clone.Policy policy;
 
         public Factory(Attributes attrs) {
-            super(attrs);
-
-            if (attrs.hasAttribute(DefaultDouble.class)) {
-                defaultValue = attrs.getAttribute(DefaultDouble.class).value();
-            } else {
-                defaultValue = 0;
-            }
+            defaultValue = attrs.hasAttribute(DefaultDouble.class) ? attrs
+                    .getAttribute(DefaultDouble.class).value() : 0.0;
+            policy = attrs.hasAttribute(Clone.class) ? attrs.getAttribute(Clone.class)
+                                                            .value()
+                                                     : Clone.Policy.JAVA_DEFAULT;
         }
 
         public Factory(double defaultValue) {
-            super(null);
             this.defaultValue = defaultValue;
+            policy = Clone.Policy.JAVA_DEFAULT;
         }
 
         @Override
         public void setDefaultValue(DoubleProperty property, int index) {
             property.set(defaultValue, index);
         }
+
+        @Override
+        public void clone(DoubleProperty src, int srcIndex, DoubleProperty dst,
+                          int dstIndex) {
+            switch (policy) {
+            case DISABLE:
+                // assign default value
+                setDefaultValue(dst, dstIndex);
+                break;
+            case INVOKE_CLONE:
+                // fall through, since default implementation of INVOKE_CLONE is to
+                // just function like JAVA_DEFAULT
+            case JAVA_DEFAULT:
+                dst.set(src.get(srcIndex), dstIndex);
+                break;
+            default:
+                throw new UnsupportedOperationException(
+                        "Enum value not supported: " + policy);
+            }
+        }
     }
 
     /**

src/main/java/com/lhkbob/entreri/property/FloatDataStore.java

-/*
- * Entreri, an entity-component framework in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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.lhkbob.entreri.property;
-
-/**
- * FloatDataStore is an IndexedDataStore that uses float arrays to store multi-element
- * component data.
- *
- * @author Michael Ludwig
- */
-public class FloatDataStore extends AbstractIndexedDataStore {
-    private final float[] array;
-
-    /**
-     * Create a new FloatDataStore with the given number of elements per logical
-     * component, and backed by the given array. The array's length must be a multiple of
-     * element size.
-     *
-     * @param elementSize The number of elements per component
-     * @param array       Backing array
-     *
-     * @throws IllegalArgumentException if array length is not a multiple of element size
-     */
-    public FloatDataStore(int elementSize, float[] array) {
-        super(elementSize);
-        this.array = array;
-    }
-
-    @Override
-    public long memory() {
-        return 4 * array.length;
-    }
-
-    @Override
-    public FloatDataStore create(int size) {
-        return new FloatDataStore(elementSize, new float[elementSize * size]);
-    }
-
-    @Override
-    public float[] getArray() {
-        return array;
-    }
-
-    @Override
-    protected int getArrayLength() {
-        return array.length;
-    }
-}

src/main/java/com/lhkbob/entreri/property/FloatProperty.java

 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * FloatProperty is an implementation of Property that stores a single float value.
  */
 @Factory(FloatProperty.Factory.class)
 public final class FloatProperty implements Property {
-    private FloatDataStore store;
+    private float[] data;
 
     /**
      * Create a FloatProperty.
      */
     public FloatProperty() {
-        store = new FloatDataStore(1, new float[1]);
+        data = new float[1];
     }
 
     /**
      *         with
      */
     public float[] getIndexedData() {
-        return store.getArray();
+        return data;
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public float get(int componentIndex) {
-        return store.getArray()[componentIndex];
+        return data[componentIndex];
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public void set(float val, int componentIndex) {
-        store.getArray()[componentIndex] = val;
+        data[componentIndex] = val;
     }
 
     @Override
-    public IndexedDataStore getDataStore() {
-        return store;
+    public void swap(int a, int b) {
+        float t = data[a];
+        data[a] = data[b];
+        data[b] = t;
     }
 
     @Override
-    public void setDataStore(IndexedDataStore store) {
-        if (store == null) {
-            throw new NullPointerException("Store cannot be null");
-        }
-        if (!(store instanceof FloatDataStore)) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with FloatProperty, wrong type: " +
-                    store.getClass());
-        }
+    public int getCapacity() {
+        return data.length;
+    }
 
-        FloatDataStore newStore = (FloatDataStore) store;
-        if (newStore.elementSize != this.store.elementSize) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with FloatProperty, wrong element size: " +
-                    newStore.elementSize);
-        }
-
-        this.store = newStore;
+    @Override
+    public void setCapacity(int size) {
+        data = Arrays.copyOf(data, size);
     }
 
     /**
      *
      * @author Michael Ludwig
      */
-    public static class Factory extends AbstractPropertyFactory<FloatProperty> {
+    public static class Factory implements PropertyFactory<FloatProperty> {
         private final float defaultValue;
+        private final Clone.Policy policy;
 
         public Factory(Attributes attrs) {
-            super(attrs);
-
-            if (attrs.hasAttribute(DefaultFloat.class)) {
-                defaultValue = attrs.getAttribute(DefaultFloat.class).value();
-            } else {
-                defaultValue = 0f;
-            }
+            defaultValue = attrs.hasAttribute(DefaultFloat.class) ? attrs
+                    .getAttribute(DefaultFloat.class).value() : 0f;
+            policy = attrs.hasAttribute(Clone.class) ? attrs.getAttribute(Clone.class)
+                                                            .value()
+                                                     : Clone.Policy.JAVA_DEFAULT;
         }
 
         public Factory(float defaultValue) {
-            super(null);
             this.defaultValue = defaultValue;
+            policy = Clone.Policy.JAVA_DEFAULT;
         }
 
         @Override
         public void setDefaultValue(FloatProperty property, int index) {
             property.set(defaultValue, index);
         }
+
+        @Override
+        public void clone(FloatProperty src, int srcIndex, FloatProperty dst,
+                          int dstIndex) {
+            switch (policy) {
+            case DISABLE:
+                // assign default value
+                setDefaultValue(dst, dstIndex);
+                break;
+            case INVOKE_CLONE:
+                // fall through, since default implementation of INVOKE_CLONE is to
+                // just function like JAVA_DEFAULT
+            case JAVA_DEFAULT:
+                dst.set(src.get(srcIndex), dstIndex);
+                break;
+            default:
+                throw new UnsupportedOperationException(
+                        "Enum value not supported: " + policy);
+            }
+        }
     }
 
     /**

src/main/java/com/lhkbob/entreri/property/IndexedDataStore.java

-/*
- * Entreri, an entity-component framework in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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.lhkbob.entreri.property;
-
-/**
- * IndexedDataStore is a generic data storage interface representing packed, random-access
- * data storage for a property of a ComponentData. All Components of the same type in the
- * same EntitySystem will have their properties share the IndexedDataStores so that
- * iteration will have much better cache locality, and will avoid the reorganization
- * caused by Java's garbage collector.
- *
- * @author Michael Ludwig
- */
-public interface IndexedDataStore {
-    /**
-     * Create a new data store with the same behavior as this IndexedDataStore except,
-     * that it holds enough space for <var>size</var> Properties.
-     *
-     * @param size The size, in number of properties
-     */
-    public IndexedDataStore create(int size);
-
-    /**
-     * @return The number of properties that can fit into this IndexedDataStore
-     */
-    public int size();
-
-    /**
-     * @return Memory usage estimate of this data store
-     */
-    public long memory();
-
-    /**
-     * <p/>
-     * Copy <var>len</var> property values starting at <var>srcOffset</var> from this
-     * IndexedDataStore into <var>dest</var>, placing the first property's values at
-     * <var>destOffset</var>. Both <var>srcOffset</var> and <var>destOffset</var> are in
-     * units of property, and not any underlying array.
-     * <p/>
-     * An exception should be thrown if the destination IndexedDataStore is not of the
-     * same type, or is not compatible with this IndexedDataStore.
-     *
-     * @param srcOffset  The offset of the first property to copy
-     * @param len        The number of properties to copy
-     * @param dest       The destination IndexedDataStore
-     * @param destOffset The offset into dest to place the first property's values
-     *
-     * @throws IndexOutOfBoundsException if the offsets or lengths cause out-of-bounds
-     *                                   exceptions
-     */
-    public void copy(int srcOffset, int len, IndexedDataStore dest, int destOffset);
-}

src/main/java/com/lhkbob/entreri/property/IntDataStore.java

-/*
- * Entreri, an entity-component framework in Java
- *
- * Copyright (c) 2012, Michael Ludwig
- * 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.
- *
- * 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 HOLDER 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.lhkbob.entreri.property;
-
-/**
- * IntDataStore is an IndexedDataStore that uses int arrays to store multi-element
- * component data.
- *
- * @author Michael Ludwig
- */
-public class IntDataStore extends AbstractIndexedDataStore {
-    private final int[] array;
-
-    /**
-     * Create a new IntDataStore with the given number of elements per logical component,
-     * and backed by the given array. The array's length must be a multiple of element
-     * size.
-     *
-     * @param elementSize The number of elements per component
-     * @param array       Backing array
-     *
-     * @throws IllegalArgumentException if array length is not a multiple of element size
-     */
-    public IntDataStore(int elementSize, int[] array) {
-        super(elementSize);
-        this.array = array;
-    }
-
-    @Override
-    public long memory() {
-        return 4 * array.length;
-    }
-
-    @Override
-    public IntDataStore create(int size) {
-        return new IntDataStore(elementSize, new int[elementSize * size]);
-    }
-
-    @Override
-    public int[] getArray() {
-        return array;
-    }
-
-    @Override
-    protected int getArrayLength() {
-        return array.length;
-    }
-}

src/main/java/com/lhkbob/entreri/property/IntProperty.java

 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
+import java.util.Arrays;
 
 /**
  * IntProperty is an implementation of Property that stores a single int value.
  */
 @Factory(IntProperty.Factory.class)
 public final class IntProperty implements Property {
-    private IntDataStore store;
+    private int[] data;
 
     /**
      * Create an IntProperty.
      */
     public IntProperty() {
-        store = new IntDataStore(1, new int[1]);
+        data = new int[1];
     }
 
     /**
      *         with
      */
     public int[] getIndexedData() {
-        return store.getArray();
+        return data;
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public int get(int componentIndex) {
-        return store.getArray()[componentIndex];
+        return data[componentIndex];
     }
 
     /**
      * @throws ArrayIndexOutOfBoundsException if the componentIndex is invalid
      */
     public void set(int val, int componentIndex) {
-        store.getArray()[componentIndex] = val;
+        data[componentIndex] = val;
     }
 
     @Override
-    public IndexedDataStore getDataStore() {
-        return store;
+    public void swap(int a, int b) {
+        int t = data[a];
+        data[a] = data[b];
+        data[b] = t;
     }
 
     @Override
-    public void setDataStore(IndexedDataStore store) {
-        if (store == null) {
-            throw new NullPointerException("Store cannot be null");
-        }
-        if (!(store instanceof IntDataStore)) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with IntProperty, wrong type: " +
-                    store.getClass());
-        }
+    public int getCapacity() {
+        return data.length;
+    }
 
-        IntDataStore newStore = (IntDataStore) store;
-        if (newStore.elementSize != this.store.elementSize) {
-            throw new IllegalArgumentException(
-                    "Store not compatible with IntProperty, wrong element size: " +
-                    newStore.elementSize);
-        }
-
-        this.store = newStore;