Michael Ludwig avatar Michael Ludwig committed 541d5b9

Begin significant refactoring of how components are defined and constructed

Comments (0)

Files changed (56)

             <type>jar</type>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.codehaus.janino</groupId>
+            <artifactId>janino</artifactId>
+            <version>2.6.1</version>
+        </dependency>
     </dependencies>
 </project>

src/main/java/com/lhkbob/entreri/AbstractComponent.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;
+
+/**
+ * <p/>
+ * Component represents a grouping of reusable and related states that are added to an
+ * {@link Entity}. The specific state of a component is stored
+ * and defined in {@link ComponentData} implementations. This separation is to support
+ * fast iteration over blocks of packed, managed memory. All of the component data is
+ * packed into buffers or arrays for cache locality. A single ComponentData instance can
+ * then be used to access multiple Components.
+ * <p/>
+ * Component instances represent the identity of the conceptual components, while
+ * instances of ComponentData can be configured to read and write to specific components.
+ * ComponentData's can change which component they reference multiple times throughout
+ * their life time.
+ * <p/>
+ * Component implements both {@link com.lhkbob.entreri.Ownable} and {@link
+ * com.lhkbob.entreri.Owner}. This can be used to create hierarchies of both components
+ * and entities that share a lifetime. When a component is removed from an entity, all of
+ * its owned objects are disowned. If any of them were entities or components, they are
+ * also removed from the system.
+ *
+ * @param <T> The ComponentData type defining the data of this component
+ *
+ * @author Michael Ludwig
+ */
+class AbstractComponent<T extends Component> implements Component {
+    private final ComponentRepository<T> owner;
+
+    private int index;
+    private int id;
+
+
+    /**
+     * Create a new Component stored in the given ComponentRepository, at the given array
+     * position within the ComponentRepository.
+     *
+     * @param owner The ComponentRepository owner
+     */
+    public AbstractComponent(ComponentRepository<T> owner) {
+        this.owner = owner;
+        this.index = 0;
+    }
+
+    public void setIndex(int index) {
+        this.index = index;
+        this.id = owner.getId(index);
+    }
+
+    public ComponentRepository<T> getRepository() {
+        return owner;
+    }
+
+    @Override
+    public boolean isAlive() {
+        // we have to check the index of the Component because the ComponentRepository
+        // does not make sure the data's indices stay within bounds of the repository arrays
+        if (index != 0 && index < owner.getMaxComponentIndex()) {
+            return owner.getId(index) == id;
+        }
+        return false;
+    }
+
+    @Override
+    public Entity getEntity() {
+        // if isAlive() returns false, then the entity index will also be 0,
+        // so getEntityByIndex() returns null, which is expected
+        int entityIndex = owner.getEntityIndex(index);
+        return owner.getEntitySystem().getEntityByIndex(entityIndex);
+    }
+
+    @Override
+    public EntitySystem getEntitySystem() {
+        return owner.getEntitySystem();
+    }
+
+    @Override
+    public final int getVersion() {
+        return owner.getVersion(index);
+    }
+
+    @Override
+    public final void updateVersion() {
+        if (isAlive()) {
+            owner.incrementVersion(index);
+        }
+    }
+
+    @Override
+    public int getIndex() {
+        return index;
+    }
+
+    @Override
+    public void notifyOwnershipGranted(Ownable obj) {
+        owner.getOwnerDelegate(index).notifyOwnershipGranted(obj);
+    }
+
+    @Override
+    public void notifyOwnershipRevoked(Ownable obj) {
+        owner.getOwnerDelegate(index).notifyOwnershipRevoked(obj);
+    }
+
+    @Override
+    public void setOwner(Owner owner) {
+        this.owner.getOwnerDelegate(index).setOwner(owner);
+    }
+
+    @Override
+    public Owner getOwner() {
+        return owner.getOwnerDelegate(index).getOwner();
+    }
+
+    @Override
+    public String toString() {
+        if (index == 0) {
+            return "Component(" + getClass().getSimpleName() + ")";
+        } else {
+            int entityId = owner.getEntitySystem()
+                                .getEntityByIndex(owner.getEntityIndex(index)).getId();
+            return "Component(" + getClass().getSimpleName() + ", entity=" + entityId +
+                   ")";
+        }
+    }
+}

src/main/java/com/lhkbob/entreri/Attribute.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;
-
-import com.lhkbob.entreri.property.IntProperty;
-import com.lhkbob.entreri.property.IntProperty.DefaultInt;
-
-import java.lang.annotation.*;
-
-/**
- * Attribute is used to declare that other annotation types are 'attributes' of a Property
- * declaration in a ComponentData type definition. They can then be accessed by custom
- * {@link PropertyFactory factories} to configure the specific property. Examples include
- * describing cloning behavior and default values.
- *
- * @author Michael Ludwig
- * @see IntProperty.Factory
- * @see DefaultInt
- */
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.ANNOTATION_TYPE)
-public @interface Attribute {
-}

src/main/java/com/lhkbob/entreri/Attributes.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;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * Attributes represents the collection of attributes that have been provided on a
- * property declaration within a ComponentData definition. To work with {@link
- * ReflectionComponentDataFactory}, {@link PropertyFactory} implementations should have a
- * constructor that takes a single Attributes instance.
- *
- * @author Michael Ludwig
- * @see PropertyFactory
- */
-public class Attributes {
-    private final Map<Class<? extends Annotation>, Annotation> attrs;
-
-    /**
-     * Construct a new set of attributes from the given annotations. Only annotations that
-     * have the Attribute annotation are kept.
-     *
-     * @param attrs
-     *
-     * @throws NullPointerException if attrs is null or contains null elements
-     */
-    public Attributes(Annotation... attrs) {
-        if (attrs == null) {
-            throw new NullPointerException("Attributes cannot be null");
-        }
-        this.attrs = new HashMap<Class<? extends Annotation>, Annotation>();
-
-        for (Annotation a : attrs) {
-            if (a.annotationType().getAnnotation(Attribute.class) != null) {
-                // the attribute is an annotation
-                this.attrs.put(a.annotationType(), a);
-            }
-        }
-    }
-
-    /**
-     * Create a new Attributes that collects all annotations that have been annotated with
-     * {@link Attribute} on the given field.
-     *
-     * @param f The field to build the set of attributes from
-     *
-     * @throws NullPointerException if f is null
-     */
-    public Attributes(Field f) {
-        this(f.getAnnotations());
-    }
-
-    /**
-     * Get the attribute annotation of type T. If there is no attribute for the given
-     * type, then null is returned.
-     *
-     * @param cls The attribute annotation class type
-     *
-     * @return The associated attribute instance
-     */
-    @SuppressWarnings("unchecked")
-    public <T extends Annotation> T getAttribute(Class<T> cls) {
-        if (cls == null) {
-            throw new NullPointerException("Annotation class cannot be null");
-        }
-        return (T) attrs.get(cls);
-    }
-
-    /**
-     * Get whether or not this set of attributes has an attribute of the given type. If an
-     * attribute does not have any variables, this is sufficient instead of getting the
-     * actual instance.
-     *
-     * @param cls The annotation class type
-     *
-     * @return True if the associated attribute exists
-     */
-    public boolean hasAttribute(Class<? extends Annotation> cls) {
-        if (cls == null) {
-            throw new NullPointerException("Annotation class cannot be null");
-        }
-        return attrs.containsKey(cls);
-    }
-
-    /**
-     * @return All annotation attributes in this set
-     */
-    public Collection<Annotation> getAttributes() {
-        return attrs.values();
-    }
-}

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

+package com.lhkbob.entreri;
+
+/**
+ *
+ */
+class CompiledFactoryProvider {
+}

src/main/java/com/lhkbob/entreri/Component.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;
 
 /**
  * <p/>
- * Component represents a grouping of reusable and related states that are added to an
- * {@link Entity}. The specific state of a component is stored and defined in {@link
- * ComponentData} implementations. This separation is to support fast iteration over
- * blocks of packed, managed memory. All of the component data is packed into buffers or
- * arrays for cache locality. A single ComponentData instance can then be used to access
- * multiple Components.
+ * ComponentData is used to define types of components that can be added to entities. For
+ * performance reasons, the identity of a component is represented by instances of {@link
+ * com.lhkbob.entreri.Component}. ComponentData instances are used as views into the data of the components.
+ * This allows multiple instances to have their data packed together in primitive arrays
+ * or direct memory, allowing for significantly faster iteration.
  * <p/>
- * Component instances represent the identity of the conceptual components, while
- * instances of ComponentData can be configured to read and write to specific components.
- * ComponentData's can change which component they reference multiple times throughout
- * their life time.
  * <p/>
- * Component implements both {@link Ownable} and {@link Owner}. This can be used to create
- * hierarchies of both components and entities that share a lifetime. When a component is
- * removed from an entity, all of its owned objects are disowned. If any of them were
- * entities or components, they are also removed from the system.
+ * Additionally, by using a single ComponentData instance during the iteration, there is
+ * no need to follow the usual object references needed for each instance.
+ * <p/>
+ * <p/>
+ * ComponentData's are defined like any other class, but they are intended to be created
+ * and configured by a {@link ComponentDataFactory}. These factories may impose certain
+ * restrictions or requirements in what constructors or fields are valid. ComponentData
+ * implementations can define a default ComponentDataFactory type with the {@link
+ * DefaultFactory} annotation. By default ComponentData implementations are created using
+ * The {@link ReflectionComponentDataFactory}
  *
- * @param <T> The ComponentData type defining the data of this component
+ * @param <T> The self-referencing type of the ComponentData
  *
  * @author Michael Ludwig
  */
-public final class Component<T extends ComponentData<T>> implements Ownable, Owner {
-    private final ComponentRepository<T> owner;
-
-    final OwnerSupport delegate;
-
-    int index;
-
+public interface Component extends Owner, Ownable {
     /**
-     * Create a new Component stored in the given ComponentRepository, at the given array
-     * position within the ComponentRepository.
-     *
-     * @param owner The ComponentRepository owner
-     * @param index The index within the owner
+     * @return The EntitySystem that created this component
      */
-    Component(ComponentRepository<T> owner, int index) {
-        this.owner = owner;
-        this.index = index;
-        delegate = new OwnerSupport(this);
-    }
-
-    /**
-     * Get a ComponentData instance that can be used to manipulate the state of this
-     * component. This is a convenience for allocating a new ComponentData instance and
-     * assigning it to this component. For tight loops, it is better to allocate a single
-     * ComponentData instance and use its {@link ComponentData#set(Component) set}
-     * method.
-     *
-     * @return A ComponentData to access this component's state, or null if the component
-     *         is not live
-     */
-    public T getData() {
-        T data = getEntitySystem().createDataInstance(getType());
-        if (data.set(this)) {
-            return data;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * @return True if the component is still attached to an entity in the entity system,
-     *         or false if it or its entity has been removed
-     */
-    public boolean isLive() {
-        return index != 0;
-    }
-
-    /**
-     * Return true if this component is enabled, or false if it is disabled and will
-     * appear as though it doesn't exist under default behavior. False is always returned
-     * if the component is not live.
-     *
-     * @return True if enabled
-     */
-    public boolean isEnabled() {
-        // if isLive() returns false, index references the 0th index, which
-        // just contains garbage
-        return isLive() && owner.isEnabled(index);
-    }
-
-    /**
-     * <p/>
-     * Set whether or not this component is enabled. If a component is disabled, default
-     * usage will cause it to appear as the component has been removed. It will not be
-     * returned from {@link Entity#get(Class)} or be included in iterator results using
-     * {@link ComponentIterator}.
-     * <p/>
-     * Disabling and enabling components can be a more efficient way to simulate the
-     * adding and removing of components, because it does not remove or require the
-     * allocation of new data.
-     *
-     * @param enable True if the component is to be enabled
-     */
-    public void setEnabled(boolean enable) {
-        // if isLive() returns false, index references the 0th index, which
-        // just contains garbage so this setter is safe
-        owner.setEnabled(index, enable);
-    }
+    public EntitySystem getEntitySystem();
 
     /**
      * Get the entity that this component is attached to. If the component has been
      *
      * @return The owning entity, or null
      */
-    public Entity getEntity() {
-        // if isLive() returns false, then the entity index will also be 0,
-        // so getEntityByIndex() returns null, which is expected
-        int entityIndex = owner.getEntityIndex(index);
-        return owner.getEntitySystem().getEntityByIndex(entityIndex);
-    }
+    public Entity getEntity();
 
     /**
-     * @return The EntitySystem that created this component
+     * @return True if the component is still attached to an entity in the entity system,
+     *         or false if it or its entity has been removed
      */
-    public EntitySystem getEntitySystem() {
-        return owner.getEntitySystem();
-    }
-
-    /**
-     * @return The class type of the ComponentData for this Component
-     */
-    public Class<T> getType() {
-        return owner.getType();
-    }
+    public boolean isAlive();
 
     /**
      * Get the underlying index of this component used to access its properties. In most
      *
      * @return The current index of component
      */
-    public int getIndex() {
-        return index;
-    }
+    public int getIndex();
 
     /**
-     * @return The ComponentRepository owning this Component
+     * <p/>
+     * Get the current version of the data accessed by this ComponentData. When data is
+     * mutated by a ComponentData, implementations increment its associated component's
+     * version so comparing a previously cached version number can be used to determine
+     * when changes have been made.
+     * <p/>
+     * Additionally, for a given component type, versions will be unique. Thus it is
+     * possible to identify when the components are replaced by new components as well.
+     *
+     * @return The current version, or a negative number if the data is invalid
      */
-    ComponentRepository<T> getRepository() {
-        return owner;
-    }
+    public int getVersion();
 
-    @Override
-    public void notifyOwnershipGranted(Ownable obj) {
-        delegate.notifyOwnershipGranted(obj);
-    }
-
-    @Override
-    public void notifyOwnershipRevoked(Ownable obj) {
-        delegate.notifyOwnershipRevoked(obj);
-    }
-
-    @Override
-    public void setOwner(Owner owner) {
-        delegate.setOwner(owner);
-    }
-
-    @Override
-    public Owner getOwner() {
-        return delegate.getOwner();
-    }
-
-    @Override
-    public String toString() {
-        if (index == 0) {
-            return "Component(" + getType().getSimpleName() + ")";
-        } else {
-            int entityId = owner.getEntitySystem()
-                                .getEntityByIndex(owner.getEntityIndex(index)).getId();
-            return "Component(" + getType().getSimpleName() + ", entity=" + entityId +
-                   ")";
-        }
-    }
+    /**
+     * Increment the version of the component accessed by this instance. It is recommended
+     * for component data implementations to call this automatically from within their
+     * exposed mutators, but if necessary it can be invoked manually as well.
+     *
+     * @see #getVersion()
+     */
+    public void updateVersion();
 }

src/main/java/com/lhkbob/entreri/ComponentData.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;
-
-/**
- * <p/>
- * ComponentData is used to define types of components that can be added to entities. For
- * performance reasons, the identity of a component is represented by instances of {@link
- * Component}. ComponentData instances are used as views into the data of the components.
- * This allows multiple instances to have their data packed together in primitive arrays
- * or direct memory, allowing for significantly faster iteration.
- * <p/>
- * <p/>
- * Additionally, by using a single ComponentData instance during the iteration, there is
- * no need to follow the usual object references needed for each instance.
- * <p/>
- * <p/>
- * ComponentData's are defined like any other class, but they are intended to be created
- * and configured by a {@link ComponentDataFactory}. These factories may impose certain
- * restrictions or requirements in what constructors or fields are valid. ComponentData
- * implementations can define a default ComponentDataFactory type with the {@link
- * DefaultFactory} annotation. By default ComponentData implementations are created using
- * The {@link ReflectionComponentDataFactory}
- *
- * @param <T> The self-referencing type of the ComponentData
- *
- * @author Michael Ludwig
- */
-public abstract class ComponentData<T extends ComponentData<T>> {
-    private int id;
-    private int index;
-
-    // this should be considered final, but is assigned in ComponentRepository
-    // to simplify implementation constructor requirements.
-    ComponentRepository<T> owner;
-
-    protected ComponentData() {
-    }
-
-    /**
-     * Get the Entity that owns this ComponentData. This is a convenience for
-     * <code>getComponent().getEntity()</code>. This should not be invoked if {@link
-     * #isValid()} returns false.
-     *
-     * @return The owning Entity
-     */
-    public final Entity getEntity() {
-        int entityIndex = owner.getEntityIndex(index);
-        return owner.getEntitySystem().getEntityByIndex(entityIndex);
-    }
-
-    /**
-     * Return the index of this ComponentData within the IndexedDataStores that back the
-     * defined properties of a ComponentData. A ComponentData's index will change as calls
-     * to {@link #set(Component)} are made.
-     *
-     * @return The index of the component used to access its IndexedDataStores.
-     */
-    public final int getIndex() {
-        return index;
-    }
-
-    /**
-     * <p/>
-     * Return whether or not this ComponentData is still attached to the last Component
-     * passed into {@link #set(Component)}, and that that component is still live.
-     * <p/>
-     * It is possible for a ComponentData's component to be invalidated underneath it.
-     * This happens if the component or its owning entity is removed from the system, or
-     * if the entity system is {@link EntitySystem#compact() compacted}. Since this
-     * library is meant to be single-threaded (or externally synchronized), this should be
-     * predictable.
-     *
-     * @return True if this ComponentData is attached to the data of a Component that is
-     *         still live
-     */
-    public final boolean isValid() {
-        // we have to check the index of the ComponentData because the ComponentRepository
-        // does not make sure the data's indices stay within bounds of the repository arrays
-        if (index != 0 && index < owner.getMaxComponentIndex()) {
-            return owner.getId(index) == id;
-        }
-        return false;
-    }
-
-    /**
-     * Return whether or not the component this data is attached to is enabled. This is an
-     * optimized shortcut for <code>getComponent().isEnabled()</code>
-     *
-     * @return True if the component is enabled, false if disabled or invalid
-     */
-    public final boolean isEnabled() {
-        return owner.isEnabled(index);
-    }
-
-    /**
-     * Set whether or not the component this data is attached to is enabled. This is an
-     * optimized shortcut for <code>getComponent().setEnabled(enable)</code>
-     *
-     * @param enable True if the component should be enabled
-     */
-    public final void setEnabled(boolean enable) {
-        owner.setEnabled(index, enable);
-    }
-
-    /**
-     * <p/>
-     * Get the current version of the data accessed by this ComponentData. When data is
-     * mutated by a ComponentData, implementations increment its associated component's
-     * version so comparing a previously cached version number can be used to determine
-     * when changes have been made.
-     * <p/>
-     * Additionally, for a given component type, versions will be unique. Thus it is
-     * possible to identify when the components are replaced by new components as well.
-     *
-     * @return The current version, or a negative number if the data is invalid
-     */
-    public final int getVersion() {
-        return owner.getVersion(index);
-    }
-
-    /**
-     * Increment the version of the component accessed by this instance. It is recommended
-     * for component data implementations to call this automatically from within their
-     * exposed mutators, but if necessary it can be invoked manually as well.
-     *
-     * @see #getVersion()
-     */
-    public final void updateVersion() {
-        if (isValid()) {
-            owner.incrementVersion(index);
-        }
-    }
-
-    /**
-     * Return the Component that this data reads and writes to. If {@link #isValid()}
-     * returns false, the returned Component is undefined. It may be the proper component,
-     * another component in the system, or null, or throw an exception.
-     *
-     * @return The component this data is attached to
-     */
-    public final Component<T> getComponent() {
-        return owner.getComponent(index);
-    }
-
-    /**
-     * <p/>
-     * Set this ComponentData to read and write from the given Component. If the component
-     * reference is a non-null, live component, this ComponentData will be considered a
-     * valid ComponentData. While valid, its defined accessors and mutators will affect
-     * the property state of <var>component</var>.
-     * <p/>
-     * Invoking set() with another Component will shift this data to the new component,
-     * allowing it to mutate that.
-     * <p/>
-     * The ComponentData will remain valid until an action is taken that would invalidate
-     * the data or its component. At the moment, the only actions capable of this are
-     * removing the component or its entity from the system, or invoking {@link
-     * EntitySystem#compact()}.
-     *
-     * @param component The component this data should point to
-     *
-     * @return True if the data is now valid
-     *
-     * @throws IllegalArgumentException if component was not created by the same entity
-     *                                  system
-     */
-    public final boolean set(Component<T> component) {
-        if (component == null) {
-            return setFast(0);
-        } else {
-            // we check repository since it is guaranteed type safe
-            if (component.getRepository() != owner) {
-                throw new IllegalArgumentException(
-                        "Component not created by expected EntitySystem");
-            }
-
-            return setFast(component.index);
-        }
-    }
-
-    @Override
-    public String toString() {
-        if (index == 0) {
-            return "ComponentData(" + getClass().getSimpleName() + ")";
-        } else {
-            int entityId = owner.getEntitySystem()
-                                .getEntityByIndex(owner.getEntityIndex(index)).getId();
-            return "ComponentData(" + getClass().getSimpleName() + ", entity=" +
-                   entityId + ")";
-        }
-    }
-
-    /**
-     * Event hook called when this ComponentData is assigned to a valid component at the
-     * provided non-zero index.
-     *
-     * @param index The new index
-     */
-    protected void onSet(int index) {
-        // do nothing in base class
-    }
-
-    /**
-     * A slightly faster method that requires only an index to a component, and performs
-     * no validation. It also does not look up the component reference since it assumes
-     * it's valid. These are lazily done when needed.
-     *
-     * @param componentIndex The index of the component
-     *
-     * @return True if the index is not 0
-     */
-    boolean setFast(int componentIndex) {
-        index = componentIndex;
-        id = owner.getId(index);
-        onSet(index);
-        return index != 0;
-    }
-}

src/main/java/com/lhkbob/entreri/ComponentDataFactory.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;
-
-import java.util.Map;
-import java.util.Set;
-
-/**
- * <p/>
- * ComponentDataFactory is a factory interface used to create instances of ComponentData
- * to provide a flexible means to instantiate and configure ComponentData's for a specific
- * type of component. For most purposes, the default {@link ReflectionComponentDataFactory}
- * will be sufficient, unless the conventions enforced by it are too restrictive.
- * <p/>
- * The main purpose of the ComponentDataFactory is to act as the glue between the set of
- * Properties representing all of the components in a system, and the ComponentData
- * instances used to efficiently access their values in a clean manner.
- *
- * @param <T> The type of ComponentData created by the factory
- *
- * @author Michael Ludwig
- */
-public interface ComponentDataFactory<T extends ComponentData<T>> {
-    /**
-     * <p/>
-     * Return the named PropertyFactories that can be used to create all required
-     * Properties when configuring a new instance. The key of the map should be the same
-     * key that is specified when {@link #setProperty(ComponentData, Object, Property)} is
-     * called.
-     * <p/>
-     * An example of the keys might be the field names declared in the class.
-     *
-     * @return The PropertyFactories created by this builder
-     */
-    public Map<?, PropertyFactory<?>> getPropertyFactories();
-
-    /**
-     * Construct a new instance of T that has not been configured. This means it should
-     * not have any assigned properties, and to expect subsequent calls to {@link
-     * #setProperty(ComponentData, Object, Property)} to configure it.
-     *
-     * @return A new instance
-     */
-    public T createInstance();
-
-    /**
-     * Inject the given property into the instance, where the property is assumed to have
-     * been constructed by PropertyFactory from {@link #getPropertyFactories()} that is
-     * stored by <var>key</var>.
-     *
-     * @param instance The instance to configure
-     * @param key      The key to the creating PropertyFactory or source of the property
-     * @param property The property instance to inject
-     *
-     * @throws NullPointerException if any argument is null
-     */
-    public void setProperty(T instance, Object key, Property property);
-
-    /**
-     * Get any additional component types that are required by the component type created
-     * by this factory instance. Most likely this will involve inspecting the type for a
-     * {@link Requires} annotation, but factory's might provide some other way of
-     * specifying or determining required component types.
-     *
-     * @return The set of required component types, or empty if no other types are
-     *         required
-     */
-    public Set<Class<? extends ComponentData<?>>> getRequiredComponentTypes();
-}

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

+package com.lhkbob.entreri;
+
+import java.util.List;
+import java.util.Set;
+
+/**
+ *
+ */
+abstract class ComponentFactoryProvider {
+    public static interface Factory<T extends Component> {
+        public AbstractComponent<T> newInstance(ComponentRepository<T> forRepository);
+
+        public List<PropertySpecification> getSpecification();
+
+        public Set<Class<? extends Component>> getRequiredTypes();
+    }
+
+    public abstract <T extends Component> Factory<T> getFactory(Class<T> componentType);
+
+    public static ComponentFactoryProvider getInstance() {
+        // FIXME impl
+        throw new UnsupportedOperationException();
+    }
+}

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

  */
 public class ComponentIterator {
     private final EntitySystem system;
-    private boolean ignoreEnabled;
 
     private int index;
 
-    private ComponentData<?>[] required; // all required except primary
-    private ComponentData<?>[] optional;
+    private Component[] required; // all required except primary
+    private Component[] optional;
 
-    private ComponentData<?> primary;
+    private Component primary;
 
     /**
      * Create a new ComponentIterator that will iterate over components or entities within
             throw new NullPointerException("System cannot be null");
         }
         this.system = system;
-        required = new ComponentData<?>[0];
-        optional = new ComponentData<?>[0];
+        required = new Component[0];
+        optional = new Component[0];
         primary = null;
         index = 0;
-        ignoreEnabled = false;
-    }
-
-    /**
-     * Set whether or not the enabled status of a component is ignored. If true, disabled
-     * components will be considered by this iterator. If false, disabled components will
-     * act as though they don't exist. This is equivalent to {@link Entity#get(Class,
-     * boolean)}.
-     *
-     * @param e The enable flag to set
-     *
-     * @return This iterator for chaining purposes
-     */
-    public ComponentIterator setIgnoreEnabled(boolean e) {
-        ignoreEnabled = e;
-        return this;
     }
 
     /**
      * @throws IllegalArgumentException if data was not created by the EntitySystem of
      *                                  this iterator
      */
-    public ComponentIterator addRequired(ComponentData<?> data) {
-        if (data == null) {
-            throw new NullPointerException("ComponentData cannot be null");
+    public <T extends Component> T addRequired(Class<T> type) {
+        if (type == null) {
+            throw new NullPointerException("Component type cannot be null");
         }
-        if (data.owner.getEntitySystem() != system) {
-            throw new IllegalArgumentException(
-                    "ComponentData not created by correct EntitySystem");
-        }
+        T data = system.getRepository(type).createDataInstance();
 
         // check to see if the data should be the new primary
         if (primary == null) {
             }
         }
 
-        return this;
+        return data;
     }
 
     /**
      * @throws IllegalArgumentException if data was not created by the EntitySystem of
      *                                  this iterator
      */
-    public ComponentIterator addOptional(ComponentData<?> data) {
-        if (data == null) {
-            throw new NullPointerException("ComponentData cannot be null");
+    public <T extends Component> T addOptional(Class<T> type) {
+        if (type == null) {
+            throw new NullPointerException("Component type cannot be null");
         }
-        if (data.owner.getEntitySystem() != system) {
-            throw new IllegalArgumentException(
-                    "ComponentData not created by correct EntitySystem");
-        }
+
+        T data = system.getRepository(type).createDataInstance();
 
         // add the data to the optional array
         optional = Arrays.copyOf(optional, optional.length + 1);
         optional[optional.length - 1] = data;
 
-        return this;
+        return data;
     }
 
     /**

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

  */
 package com.lhkbob.entreri;
 
-import com.lhkbob.entreri.property.BooleanProperty;
-import com.lhkbob.entreri.property.IntProperty;
+import com.lhkbob.entreri.property.*;
 
 import java.lang.ref.WeakReference;
 import java.util.*;
-import java.util.Map.Entry;
 
 /**
  * ComponentRepository manages storing all the componentDatas of a specific type for an
  *
  * @author Michael Ludwig
  */
-final class ComponentRepository<T extends ComponentData<T>> {
+@SuppressWarnings({ "unchecked", "rawtypes" })
+final class ComponentRepository<T extends Component> {
     private final EntitySystem system;
     private final Class<T> type;
 
-    private final ComponentDataFactory<T> factory;
-    private final Class<? extends ComponentData<?>>[] requiredTypes;
+    private final ComponentFactoryProvider.Factory<T> factory;
+    private final Class<? extends Component>[] requiredTypes;
 
     // These three arrays have a special value of 0 or null stored in the 0th
     // index, which allows us to lookup componentDatas or entities when they
     // normally aren't attached.
     private int[] entityIndexToComponentRepository;
     private int[] componentIndexToEntityIndex;
-    private Component<T>[] components;
+    private T[] components;
     private int componentInsert;
 
     private final List<DeclaredPropertyStore<?>> declaredProperties;
     private final List<DecoratedPropertyStore<?>> decoratedProperties;
 
     // this is contained in decoratedProperties
-    private final BooleanProperty enabledProperty;
     private final IntProperty componentIdProperty;
     private final IntProperty componentVersionProperty;
+
+    private final ObjectProperty<OwnerSupport> ownerDelegatesProperty;
+
     private int idSeq;
     private int versionSeq;
 
      *
      * @throws NullPointerException if system or type are null
      */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public ComponentRepository(EntitySystem system, Class<T> type,
-                               ComponentDataFactory<T> factory) {
+    public ComponentRepository(EntitySystem system, Class<T> type) {
         if (system == null || type == null) {
             throw new NullPointerException("Arguments cannot be null");
         }
 
         this.system = system;
-        this.factory = factory;
+        this.factory = ComponentFactoryProvider.getInstance().getFactory(type);
         this.type = type;
-        requiredTypes = factory.getRequiredComponentTypes().toArray(new Class[0]);
+        requiredTypes = factory.getRequiredTypes().toArray(new Class[factory.getRequiredTypes().size()]);
 
-        Map<?, PropertyFactory<?>> propertyFactories = factory.getPropertyFactories();
+        List<PropertySpecification> spec = factory.getSpecification();
 
         declaredProperties = new ArrayList<DeclaredPropertyStore<?>>();
         decoratedProperties = new ArrayList<DecoratedPropertyStore<?>>(); // empty for now
-        for (Entry<?, PropertyFactory<?>> e : propertyFactories.entrySet()) {
-            DeclaredPropertyStore store = new DeclaredPropertyStore(e.getValue(),
-                                                                    e.getKey());
+        for (PropertySpecification p : spec) {
+            DeclaredPropertyStore store = new DeclaredPropertyStore(p.getFactory(), p.getName());
             declaredProperties.add(store);
         }
 
         entityIndexToComponentRepository = new int[1]; // holds default 0 value in 0th index
         componentIndexToEntityIndex = new int[1]; // holds default 0 value in 0th index
-        components = new Component[1]; // holds default null value in 0th index
+        components = (T[]) new Component[1]; // holds default null value in 0th index
 
         componentInsert = 1;
 
         resizePropertyStores(declaredProperties, 1);
 
         // decorate the component data with a boolean property to track enabled status
-        enabledProperty = decorate(new BooleanProperty.Factory(true));
         // we set a unique id for every component
         componentIdProperty = decorate(new IntProperty.Factory(0));
         componentVersionProperty = decorate(new IntProperty.Factory(0));
+        ownerDelegatesProperty = decorate(ObjectProperty.<OwnerSupport>factory(null));
 
         idSeq = 1; // start at 1, just like entity id sequences versionSeq = 0;
 
-        // initialize enabled and version for the 0th index
-        enabledProperty.set(false, 0);
+        // initialize version for the 0th index
         componentVersionProperty.set(-1, 0);
     }
 
     }
 
     /**
-     * @param componentIndex The component to look up
-     *
-     * @return True if the component at componentIndex is enabled
-     */
-    public boolean isEnabled(int componentIndex) {
-        return enabledProperty.get(componentIndex);
-    }
-
-    /**
-     * Set whether or not the component at <var>componentIndex</var> is enabled. This does
-     * nothing if the index is 0, preserving the guarantee that invalid component is
-     * considered disabled.
-     *
-     * @param componentIndex The component index
-     * @param enabled        True if the component is enabled
-     */
-    public void setEnabled(int componentIndex, boolean enabled) {
-        if (componentIndex != 0) {
-            enabledProperty.set(enabled, componentIndex);
-        }
-    }
-
-    /**
      * @param componentIndex The component index
      *
      * @return The component id of the component at the given index
      * index is 0, preserving the guarantee that an invalid component has a negative
      * version.
      *
-     * @param componentIndex
+     * @param componentIndex The component to update
      */
     public void incrementVersion(int componentIndex) {
         if (componentIndex != 0) {
         }
     }
 
+    /**
+     * @param componentIndex The component index
+     * @return The OwnerSupport delegate for the component by the given index
+     */
+    public OwnerSupport getOwnerDelegate(int componentIndex) {
+        return ownerDelegatesProperty.get(componentIndex);
+    }
+
     /*
      * As expandEntityIndex() but expands all related component data and arrays
      * to hold the number of components.
      *
      * @return The component reference at the given index, may be null
      */
-    public Component<T> getComponent(int componentIndex) {
+    public T getComponent(int componentIndex) {
         return components[componentIndex];
     }
 
      * @return A new component of type T
      *
      * @throws NullPointerException     if fromTemplate is null
-     * @throws IllegalArgumentException if the template was not created by this index
      * @throws IllegalStateException    if the template is not live
      */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public Component<T> addComponent(int entityIndex, Component<T> fromTemplate) {
-        if (fromTemplate.getEntitySystem() != getEntitySystem()) {
-            throw new IllegalArgumentException(
-                    "Component not owned by expected EntitySystem");
-        }
-        if (!fromTemplate.getType().equals(type)) {
+    public T addComponent(int entityIndex, T fromTemplate) {
+        if (!type.isInstance(fromTemplate)) {
             throw new IllegalArgumentException(
                     "Component not of expected type, expected: " + type + ", but was: " +
-                    fromTemplate.getType());
+                    fromTemplate.getClass());
         }
-        if (!fromTemplate.isLive()) {
+        if (!fromTemplate.isAlive()) {
             throw new IllegalStateException("Template component is not live");
         }
 
-        Component<T> instance = addComponent(entityIndex);
+        T instance = addComponent(entityIndex);
+        // this is safe across systems because the property spec for a component type
+        // will be the same so the factories will be consistent
         for (int i = 0; i < declaredProperties.size(); i++) {
             DeclaredPropertyStore store = declaredProperties.get(i);
-            store.clone(fromTemplate.index, store.property, instance.index);
+            store.clone(fromTemplate.getIndex(), store.property, instance.getIndex());
         }
 
         return instance;
      *
      * @throws IllegalArgumentException if initParams is incorrect
      */
-    public Component<T> addComponent(int entityIndex) {
-        Component<T> instance = allocateComponent(entityIndex);
-        return instance;
-    }
-
-    /*
-     * Allocate and store a new component and initialize it to its default state
-     */
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    private Component<T> allocateComponent(int entityIndex) {
+    public T addComponent(int entityIndex) {
         if (entityIndexToComponentRepository[entityIndex] != 0) {
             removeComponent(entityIndex);
         }
             expandComponentRepository(componentIndex + 1);
         }
 
-        Component<T> instance = new Component<T>(this, componentIndex);
+        T instance = createDataInstance(componentIndex);
         components[componentIndex] = instance;
         componentIndexToEntityIndex[componentIndex] = entityIndex;
         entityIndexToComponentRepository[entityIndex] = componentIndex;
         for (int i = 0; i < declaredProperties.size(); i++) {
             declaredProperties.get(i).setDefaultValue(componentIndex);
         }
-
         for (int i = 0; i < decoratedProperties.size(); i++) {
             decoratedProperties.get(i).setDefaultValue(componentIndex);
         }
         // although there could be a custom PropertyFactory for setting the id,
         // it's easier to assign a new id here
         componentIdProperty.set(idSeq++, componentIndex);
+        // same goes for assigning a new owner delegate
+        ownerDelegatesProperty.set(new OwnerSupport(instance), componentIndex);
 
         // start with a unique version as well
         incrementVersion(componentIndex);
         Entity entity = system.getEntityByIndex(entityIndex);
         for (int i = 0; i < requiredTypes.length; i++) {
             if (entity.get((Class) requiredTypes[i]) == null) {
-                Component<?> added = entity.add((Class) requiredTypes[i]);
+                Component added = entity.add((Class) requiredTypes[i]);
                 added.setOwner(instance);
             }
         }
      * @return A new data instance
      */
     public T createDataInstance() {
+        return createDataInstance(0);
+    }
+
+    private T createDataInstance(int forIndex) {
         // create a new instance from the factory - it will be completely detached
-        T t = factory.createInstance();
+        AbstractComponent<T> t = factory.newInstance(this);
 
-        // attach it to this data index, at the 0th index
-        //  - at this point the ComponentData's owner should be considered final
-        t.owner = this;
-
-        // assign all property values
-        for (int i = 0; i < declaredProperties.size(); i++) {
-            DeclaredPropertyStore<?> p = declaredProperties.get(i);
-            factory.setProperty(t, p.key, p.property);
-        }
-
-        t.set(null);
-        return t;
+        t.setIndex(forIndex);
+        return (T) t;
     }
 
     /**
         int componentIndex = entityIndexToComponentRepository[entityIndex];
 
         // This code works even if componentIndex is 0
-        Component<T> oldComponent = components[componentIndex];
+        T oldComponent = components[componentIndex];
+        AbstractComponent<T> casted = (AbstractComponent<T>) oldComponent;
         if (oldComponent != null) {
             oldComponent.setOwner(null);
-            oldComponent.delegate.disownAndRemoveChildren();
-            oldComponent.index = 0;
+            casted.delegate.disownAndRemoveChildren();
+            casted.setIndex(0);
         }
 
         components[componentIndex] = null;
         entityIndexToComponentRepository[entityIndex] = 0; // entity does not have component
         componentIndexToEntityIndex[componentIndex] = 0; // component does not have entity
         componentIdProperty.set(0, componentIndex);
-        enabledProperty.set(false, componentIndex);
 
         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,
-                        Component<T>[] newToOldMap) {
+    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, newToOldMap));
+                p.setDataStore(update(origStore, store.swap));
                 store.swap = origStore;
             }
         }
 
     /*
      * Update all component data in src to be in dst by shuffling it to match
-     * newToOldMap.
+     * current state of components array.
      */
-    private IndexedDataStore update(IndexedDataStore src, IndexedDataStore dst,
-                                    Component<T>[] newToOldMap) {
-        int dstSize = newToOldMap.length;
+    private IndexedDataStore update(IndexedDataStore src, IndexedDataStore dst) {
+        int dstSize = components.length;
 
         if (dst == null || dst.size() < dstSize) {
             dst = src.create(dstSize);
         int copyIndexNew = -1;
         int copyIndexOld = -1;
         for (i = 1; i < componentInsert; i++) {
-            if (newToOldMap[i] == null) {
+            if (components[i] == null) {
                 // we've hit the end of existing componentDatas, so break
                 break;
             }
 
-            if (newToOldMap[i].index != lastIndex + 1) {
+            if (components[i].getIndex() != lastIndex + 1) {
                 // we are not in a contiguous section
                 if (copyIndexOld >= 0) {
                     // we have to copy over the last section
 
                 // set the copy indices
                 copyIndexNew = i;
-                copyIndexOld = newToOldMap[i].index;
+                copyIndexOld = components[i].getIndex();
             }
-            lastIndex = newToOldMap[i].index;
+            lastIndex = components[i].getIndex();
         }
 
         if (copyIndexOld >= 0) {
      * @param numEntities       The number of entities that are in the system
      */
     public void compact(int[] entityOldToNewMap, int numEntities) {
-        // First sort the canonical componentDatas array
-        Arrays.sort(components, 1, componentInsert, new Comparator<Component<T>>() {
+        // First sort the canonical components array to order them by their entity
+        Arrays.sort(components, 1, componentInsert, new Comparator<T>() {
             @Override
-            public int compare(Component<T> o1, Component<T> o2) {
+            public int compare(T o1, T o2) {
                 if (o1 != null && o2 != null) {
-                    return componentIndexToEntityIndex[o1.index] -
-                           componentIndexToEntityIndex[o2.index];
+                    return componentIndexToEntityIndex[o1.getIndex()] -
+                           componentIndexToEntityIndex[o2.getIndex()];
                 } else if (o1 != null) {
                     return -1; // push null o2 to end of array
                 } else if (o2 != null) {
             }
         }
 
-        // Update all of the property stores to match up with the componentDatas new positions
-        update(declaredProperties, components);
-        update(decoratedProperties, components);
+        // Update all of the property stores to match up with the components new positions
+        update(declaredProperties);
+        update(decoratedProperties);
 
         // 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].index]];
-                components[i].index = i;
+                newComponentRepository[i] = entityOldToNewMap[componentIndexToEntityIndex[components[i].getIndex()]];
+                ((AbstractComponent<T>) components[i]).setIndex(i);
                 componentInsert = i + 1;
             }
         }

src/main/java/com/lhkbob/entreri/DefaultFactory.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;
-
-import java.lang.annotation.*;
-
-/**
- * <p/>
- * DefaultFactory is a type-level annotation that can be added to ComponentData
- * definitions to declare a different ComponentDataFactory than {@link
- * ReflectionComponentDataFactory} as the default. This default will be used by any
- * EntitySystem unless it has a per-system factory override that was set with {@link
- * EntitySystem#setFactory(Class, ComponentDataFactory)}
- * <p/>
- * <p/>
- * Runtime exceptions will be thrown if the factory type declared by the annotation does
- * not have an accessible, supported constructor. The currently supported constructors
- * are: <ol> <li><code>ComponentDataFactory()</code></li> <li><code>ComponentDataFactory(TypeId&lt;T&gt;)</code></li>
- * <li> <code>ComponentDataFactory(Class&lt;T extends ComponentData&lt;T&gt;&gt;)</code>
- * </li> </ol>
- *
- * @author Michael Ludwig
- */
-@Documented
-@Retention(RetentionPolicy.RUNTIME)
-@Target(ElementType.TYPE)
-public @interface DefaultFactory {
-    /**
-     * @return The ComponentDataFactory implementation used to create ComponentData's
-     */
-    Class<? extends ComponentDataFactory<?>> value();
-}

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

 /**
  * <p/>
  * An Entity represents a collection of Components within an EntitySystem. Entities are
- * created by calling {@link EntitySystem#addEntity()} or the similar function that takes
+ * created by calling {@link com.lhkbob.entreri.EntitySystem#addEntity()} or the similar function that takes
  * another Entity as a template.
  * <p/>
- * Entities use instance equality, just like {@link Component}. Once created the Entity
+ * Entities use instance equality, just like {@link com.lhkbob.entreri.Component}. Once created the Entity
  * object will not change its identity.
  * <p/>
  * <p/>
- * Entity implements both {@link Ownable} and {@link Owner}. This can be used to create
+ * Entity implements both {@link com.lhkbob.entreri.Ownable} and {@link com.lhkbob.entreri.Owner}. This can be used to create
  * hierarchies of both components and entities that share a lifetime. When an entity is
  * removed from the system, all of its owned objects are disowned. If any of them were
  * entities or components, they are also removed from the system.
  * @author Michael Ludwig
  */
 public final class Entity
-        implements Iterable<Component<?>>, Comparable<Entity>, Ownable, Owner {
+        implements Iterable<Component>, Comparable<Entity>, Ownable, Owner {
     private final EntitySystem system;
     private final int id;
 
      * @return True if this Entity is still in its EntitySystem, or false if it has been
      *         removed
      */
-    public boolean isLive() {
+    public boolean isAlive() {
         return index != 0;
     }
 
      * Get the Component instance of the given type that's attached to this Entity. A null
      * value is returned if the component type has not been attached to the entity, or if
      * the component is disabled.
-     * <p/>
-     * If the entity has a component of the given type, but it has been marked as
-     * disabled, this will return null. Use {@link #get(Class, boolean)} to override this
-     * behavior and return disabled components.
      *
      * @param <T>           The parameterized type of ComponentData of the component
      * @param componentType The given type
      *
      * @throws NullPointerException if id is null
      */
-    public <T extends ComponentData<T>> Component<T> get(Class<T> componentType) {
-        return get(componentType, false);
-    }
-
-    /**
-     * Get the Component instance of the given type that's attached to this entity. If
-     * <var>ignoreEnable</var> is true, then disabled components will be returned as
-     * well.
-     *
-     * @param <T>           The parameterized type of ComponentData of the component
-     * @param componentType The data type
-     * @param ignoreEnable  True if disabled components should be returned as well
-     *
-     * @return The current Component of type T attached to this entity
-     *
-     * @throws NullPointerException if componentType is null
-     */
-    public <T extends ComponentData<T>> Component<T> get(Class<T> componentType,
-                                                         boolean ignoreEnable) {
+    public <T extends Component> T get(Class<T> componentType) {
         ComponentRepository<T> ci = system.getRepository(componentType);
-        Component<T> c = ci.getComponent(ci.getComponentIndex(index));
-
-        if (c == null || ignoreEnable || c.isEnabled()) {
-            return c;
-        } else {
-            return null;
-        }
-    }
-
-    /**
-     * <p/>
-     * Get the component of type T attached to this entity, by setting the given data's
-     * reference. This will return true if the data instance has been set to a valid
-     * component and that component is enabled. This is a shortcut for:
-     * <p/>
-     * <pre>
-     * Component&lt;T&gt; c = entity.get(TypeId.get(T.class));
-     * if (c != null &amp;&amp; c.isEnabled()) {
-     *     if (data.set(c)) {
-     *         // process c via data
-     *     }
-     * }
-     * </pre>
-     * <p/>
-     * <p/>
-     * <p/>
-     * Note that there is no equivalent {@link #get(Class boolean)} that takes a
-     * ComponentData. This is because the data instance will still be set to a valid
-     * component even when it's disabled. It is possible to identify this case if get()
-     * returns false but isValid() returns true.
-     *
-     * @param <T>  The component data type
-     * @param data An instance of the data that will be set to this entity's component
-     *
-     * @return True if the data now references a valid and enabled component, and false if
-     *         the component is invalid or if the component is disabled
-     *
-     * @throws NullPointerException     if data is null
-     * @throws IllegalArgumentException if the data was created by another entity system
-     */
-    public <T extends ComponentData<T>> boolean get(T data) {
-        if (data.owner.getEntitySystem() != getEntitySystem()) {
-            throw new IllegalArgumentException(
-                    "ComponentData was not created by expected EntitySystem");
-        }
-
-        ComponentRepository<T> ci = data.owner;
-        int componentIndex = ci.getComponentIndex(index);
-        return data.setFast(componentIndex) && ci.isEnabled(componentIndex);
+        return ci.getComponent(ci.getComponentIndex(index));
     }
 
     /**
      *
      * @throws NullPointerException if componentId is null
      */
-    public <T extends ComponentData<T>> Component<T> add(Class<T> componentType) {
+    public <T extends Component> T add(Class<T> componentType) {
         ComponentRepository<T> ci = system.getRepository(componentType);
         return ci.addComponent(index);
     }
      * <p/>
      * The new component is initialized by cloning the property values from
      * <var>toClone</var> into the values of the new component. This is performed by
-     * invoking {@link PropertyFactory#clone(Property, int, Property, int)} with the
+     * invoking {@link com.lhkbob.entreri.property.PropertyFactory#clone(com.lhkbob.entreri.property.Property, int, com.lhkbob.entreri.property.Property, int)} with the
      * factories that created each property. All default property factories perform a copy
      * by value (or copy by reference for object types).
      *
      * @throws IllegalStateException    if toClone is not a live component instance
      */
     @SuppressWarnings({ "unchecked", "rawtypes" })
-    public <T extends ComponentData<T>> Component<T> add(Component<T> toClone) {
+    public <T extends Component> T add(T toClone) {
         if (toClone == null) {
             throw new NullPointerException(
                     "ComponentData template, toClone, cannot be null");
         }
-        ComponentRepository ci = system.getRepository(toClone.getType());
+        // FIXME getClass() won't return the right interface getRepo() expects
+        ComponentRepository ci = system.getRepository(toClone.getClass());
         return ci.addComponent(index, toClone);
     }
 
      * Remove any attached Component with the data type, T, from this Entity. True is
      * returned if a component was removed, and false otherwise. If a component is
      * removed, the component should no longer be used and it will return false from
-     * {@link Component#isLive()}. This will remove the component even if the component
+     * {@link Component#isAlive()}. This will remove the component even if the component
      * has been disabled.
      * <p/>
      * When a Component is removed, it will set its owner to null, and disown all of its
      *
      * @throws NullPointerException if componentId is null
      */
-    public <T extends ComponentData<T>> boolean remove(Class<T> componentType) {
+    public <T extends Component> boolean remove(Class<T> componentType) {
         ComponentRepository<T> ci = system.getRepository(componentType);
         return ci.removeComponent(index);
     }
      * Return an iterator over the components currently attached to the Entity. The
      * iterator supports the remove operation and will detach the component from the
      * entity.
-     * <p/>
-     * This will not report disabled components. Use {@link #iterator(boolean)} to
-     * override this behavior.
      *
      * @return An iterator over the entity's components
      */
     @Override
-    public Iterator<Component<?>> iterator() {
-        return iterator(false);
-    }
-
-    /**
-     * Return an iterator over the components attached to this entity. If
-     * <var>ignoreEnabled</var> is true, disabled components will be included in the
-     * iterator's results. If it is false, disabled components are not returned.
-     *
-     * @param ignoreEnabled True if disabled components are returned
-     *
-     * @return An iterator over the entity's components
-     */
-    public Iterator<Component<?>> iterator(boolean ignoreEnabled) {
-        return new ComponentIterator(system, index, ignoreEnabled);
+    public Iterator<Component> iterator() {
+        return new ComponentIterator(system, index);
     }
 
     /*
      * Iterator implementation that iterates over the components attached to an
      * entity, based on entity index rather than reference
      */
-    private static class ComponentIterator implements Iterator<Component<?>> {
+    private static class ComponentIterator implements Iterator<Component> {
         private final int entityIndex;
         private final Iterator<ComponentRepository<?>> indices;
-        private final boolean ignoreEnable;
 
         private ComponentRepository<?> currentIndex;
         private ComponentRepository<?> nextIndex;
 
-        public ComponentIterator(EntitySystem system, int entityIndex,
-                                 boolean ignoreEnable) {
+        public ComponentIterator(EntitySystem system, int entityIndex) {
             this.entityIndex = entityIndex;
-            this.ignoreEnable = ignoreEnable;
             indices = system.indexIterator();
         }
 
         }
 
         @Override
-        public Component<?> next() {
+        public Component next() {
             if (!hasNext()) {
                 throw new NoSuchElementException();
             }
                 nextIndex = indices.next();
 
                 int index = nextIndex.getComponentIndex(entityIndex);
-                if (index != 0 && (ignoreEnable || nextIndex.isEnabled(index))) {
+                if (index != 0) {
                     break;
                 } else {
                     nextIndex = null; // must set to null if this was last element
         StringBuilder sb = new StringBuilder("Entity(");
         sb.append(id);
 
-        Iterator<Component<?>> it = iterator();
+        Iterator<Component> it = iterator();
         while (it.hasNext()) {
             sb.append(", ");
-            sb.append(it.next().getType().getSimpleName());
+            sb.append(it.next().getClass().getSimpleName());
         }
 
         sb.append(")");

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

  */
 package com.lhkbob.entreri;
 
+import com.lhkbob.entreri.property.Property;
+import com.lhkbob.entreri.property.PropertyFactory;
 import com.lhkbob.entreri.task.Scheduler;
 import com.lhkbob.entreri.task.Task;
 
-import java.lang.reflect.Constructor;
 import java.util.*;
 
 /**
  * @author Michael Ludwig
  * @see Entity
  * @see Component
- * @see ComponentData
  */
 public final class EntitySystem implements Iterable<Entity> {
     // converts valid component data types into indices into componentRepositories
-    private final Map<Class<? extends ComponentData<?>>, Integer> typeIndexMap;
+    private final Map<Class<? extends Component>, Integer> typeIndexMap;
     private int typeIdSeq;
 
     private ComponentRepository<?>[] componentRepositories;
      * Create a new EntitySystem that has no entities added.
      */
     public EntitySystem() {
-        typeIndexMap = new HashMap<Class<? extends ComponentData<?>>, Integer>();
+        typeIndexMap = new HashMap<Class<? extends Component>, Integer>();
         typeIdSeq = 0;
 
         manager = new Scheduler(this);
     }
 
     /**
-     * <p/>
-     * Assign a specific ComponentDataFactory instance to create and manage ComponentData
-     * instances of the given type for this system. This will override the default {@link
-     * ReflectionComponentDataFactory} or any default type declared by the {@link
-     * DefaultFactory} annotation.
-     * <p/>
-     * However, a ComponentDataFactory cannot be assigned if there is already another
-     * factory in use for that type. This situation arises if setFactory() is called
-     * multiple times, or if the EntitySystem needs to use a given type before
-     * setFactory() is called and it must fall back onto a default factory.
-     * <p/>
-     * This rule exists to ensure that all ComponentData instances of a given type created
-     * by an EntitySystem come from the same factory, regardless of when they were
-     * instantiated.
-     *
-     * @param <T>     The ComponentData type created by the factory
-     * @param type    The type of the component type
-     * @param factory The factory to use in this system for the given type
-     *
-     * @throws NullPointerException     if id or factory are null
-     * @throws IllegalArgumentException if the factory does not actually create instances
-     *                                  of type T
-     * @throws IllegalStateException    if the EntitySystem already has a factory for the
-     *                                  given type
-     */
-    @SuppressWarnings("unchecked")
-    public <T extends ComponentData<T>> void setFactory(Class<T> type,
-                                                        ComponentDataFactory<T> factory) {
-        if (type == null) {
-            throw new NullPointerException("TypeId cannot be null");
-        }
-        if (factory == null) {
-            throw new NullPointerException("ComponentDataFactory cannot be null");
-        }
-
-        int index = getTypeIndex(type);
-        if (index >= componentRepositories.length) {
-            // make sure it's the correct size
-            componentRepositories = Arrays.copyOf(componentRepositories, index + 1);
-        }
-
-        ComponentRepository<T> i = (ComponentRepository<T>) componentRepositories[index];
-        if (i != null) {
-            // a factory is already defined
-            throw new IllegalStateException(
-                    "A ComponentDataFactory is already assigned to the type: " + type);
-        }
-