Commits

Michael Ludwig committed 89fabe2

Rename packages to com.lhkbob

Comments (0)

Files changed (122)

src/main/java/com/googlecode/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.googlecode.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>
- * <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>
- * 
- * @author Michael Ludwig
- * @param <T> The ComponentData type defining the data of this component
- */
-public final class Component<T extends ComponentData<T>> {
-    private final ComponentRepository<T> owner;
-    
-    int index;
-
-    /**
-     * 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
-     */
-    Component(ComponentRepository<T> owner, int index) {
-        this.owner = owner;
-        this.index = index;
-    }
-
-    /**
-     * 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(getTypeId());
-        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
-     */
-    public boolean isEnabled() {
-        // if isLive() returns false, index references the 0th index, which
-        // just contains garbage
-        return 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(TypeId)} or be included in
-     * iterator results using {@link ComponentIterator}.
-     * </p>
-     * <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.
-     * </p>
-     * 
-     * @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);
-    }
-
-    /**
-     * Get the entity that this component is attached to. If the component has
-     * been removed from the entity, or is otherwise not live, this will return
-     * null.
-     * 
-     * @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);
-    }
-    
-    /**
-     * @return The EntitySystem that created this component
-     */
-    public EntitySystem getEntitySystem() {
-        return owner.getEntitySystem();
-    }
-    
-    /**
-     * @return The TypeId of the ComponentData for this Component
-     */
-    public TypeId<T> getTypeId() {
-        return owner.getTypeId();
-    }
-    
-    /**
-     * @return The ComponentRepository owning this Component
-     */
-    ComponentRepository<T> getRepository() {
-        return owner;
-    }
-}

src/main/java/com/googlecode/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.googlecode.entreri;
-
-import com.googlecode.entreri.annot.DefaultFactory;
-import com.googlecode.entreri.property.ReflectionComponentDataFactory;
-
-/**
- * <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} annoation. By
- * default ComponentData implementations are created using The
- * {@link ReflectionComponentDataFactory}
- * </p>
- * 
- * @author Michael Ludwig
- * @param <T> The self-referencing type of the ComponentData
- */
-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>
-     * <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.
-     * </p>
-     * 
-     * @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
-     */
-    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);
-    }
-
-    /**
-     * 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
-     * <tt>component</tt>.
-     * </p>
-     * <p>
-     * Invoking set() with another Component will shift this data to the new
-     * component, allowing it to mutate that.
-     * </p>
-     * <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()}.
-     * </p>
-     * 
-     * @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);
-        }
-    }
-
-    /**
-     * 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);
-        return index != 0;
-    }
-}

src/main/java/com/googlecode/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.googlecode.entreri;
-
-import java.util.Map;
-
-import com.googlecode.entreri.property.Property;
-import com.googlecode.entreri.property.PropertyFactory;
-import com.googlecode.entreri.property.ReflectionComponentDataFactory;
-
-/**
- * <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>
- * <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.
- * </p>
- * 
- * @author Michael Ludwig
- * @param <T> The type of ComponentData created by the factory
- */
-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>
-     * <p>
-     * An example of the keys might be the field names declared in the class.
-     * </p>
-     * 
-     * @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 <tt>key</tt>.
-     * 
-     * @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);
-}

src/main/java/com/googlecode/entreri/ComponentIterator.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.googlecode.entreri;
-
-import java.util.Arrays;
-import java.util.Iterator;
-
-/**
- * <p>
- * ComponentIterator is an {@link Iterator}-like class used for quickly
- * iterating over subsets of entities within an EntitySystem. Required and
- * optional ComponentData instances are added to it during initialization. These
- * then determine the constraints on the entities reported by the iterator.
- * ComponentIterator is reset-able, so the same instance can be reused for
- * multiple iterations.
- * </p>
- * <p>
- * The ComponentIterator will skip all entities that do not have components of
- * the required component types. This is very useful for Controllers because
- * they often have related component types that they want to fetch at the same
- * time. Optional ComponentData's will be updated if present, and will otherwise
- * be flagged as invalid.
- * </p>
- * <p>
- * The basic workflow for using a ComponentIterator is shown below. In the
- * example, the iterator is used to iterate over all entities that have both an
- * A and a B component, while optionally loading a C component if available.
- * 
- * <pre>
- * // create ComponentData instances
- * A cdA = system.createDataInstance(TypeId.get(A.class));
- * B cdB = system.createDataInstance(TypeId.get(B.class));
- * C cdC = system.createDataInstance(TypeId.get(C.class));
- * 
- * // initialize iterator
- * ComponentIterator it = new ComponentIterator(system);
- * it.addRequired(cdA).addRequired(cdB).addOptional(cdC);
- * it.reset(); // not actually required for first iteration
- * 
- * // iterate
- * while(it.next()) {
- *    // cdA and cdB are both assigned to components of type A and B on the same
- *    // entity, so they can be processed without checking isValid()
- *    ...
- *    // cdC may or may not be valid
- *    if (cdC.isValid()) {
- *       // the current entity also has a component of type C
- *    }
- * }
- * </pre>
- * 
- * </p>
- * 
- * @author Michael Ludwig
- */
-public class ComponentIterator {
-    private final EntitySystem system;
-    
-    private int index;
-    
-    private ComponentData<?>[] required; // all required except primary
-    private ComponentData<?>[] optional;
-    
-    private ComponentData<?> primary;
-
-    /**
-     * Create a new ComponentIterator that will iterate over components or
-     * entities within the given EntitySystem. It will have no attached required
-     * or optional ComponentDatas.
-     * 
-     * @param system The EntitySystem of the iterator
-     * @throws NullPointerException if system is null
-     */
-    public ComponentIterator(EntitySystem system) {
-        if (system == null)
-            throw new NullPointerException("System cannot be null");
-        this.system = system;
-        required = new ComponentData<?>[0];
-        optional = new ComponentData<?>[0];
-        primary = null;
-        index = 0;
-    }
-
-    /**
-     * Add the given ComponentData instance as a required component type for
-     * this iterator. Besides implicitly specifying the required type, the
-     * provided instance will be set to each component of that type during
-     * iteration. Thus it is recommended to hold onto instance for later use.
-     * 
-     * @param data The ComponentData that is used to access the required
-     *            component type data of its type
-     * @return This iterator to chain
-     * @throws NullPointerException if data is null
-     * @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");
-        if (data.owner.getEntitySystem() != system)
-            throw new IllegalArgumentException("ComponentData not created by correct EntitySystem");
-        
-        // check to see if the data should be the new primary
-        if (primary == null) {
-            // no other required components, so just set it
-            primary = data;
-        } else {
-            // check if the new data is shorter, but we will definitely
-            // putting one data into the required array
-            required = Arrays.copyOf(required, required.length + 1);
-
-            if (data.owner.getMaxComponentIndex() < primary.owner.getMaxComponentIndex()) {
-                // new primary
-                required[required.length - 1] = primary;
-                primary = data;
-            } else {
-                // not short enough so store it in the array
-                required[required.length - 1] = data;
-            }
-        }
-        
-        return this;
-    }
-
-    /**
-     * <p>
-     * Add the given ComponentData instance as an optional component type for
-     * this iterator. Besides implicitly specifying the required type, the
-     * provided instance will be set to each component of that type during
-     * iteration. Thus it is recommended to hold onto instance for later use.
-     * </p>
-     * <p>
-     * Entities are not required to have components of this type when reported
-     * by this iterator. It is important to check
-     * {@link ComponentData#isValid()} first.
-     * </p>
-     * 
-     * @param data The ComponentData that is used to access the required
-     *            component type data of its type
-     * @return This iterator to chain
-     * @throws NullPointerException if data is null
-     * @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");
-        if (data.owner.getEntitySystem() != system)
-            throw new IllegalArgumentException("ComponentData not created by correct EntitySystem");
-
-        // add the data to the optional array
-        optional = Arrays.copyOf(optional, optional.length + 1);
-        optional[optional.length - 1] = data;
-        
-        return this;
-    }
-
-    /**
-     * <p>
-     * Advance the iterator to the next Entity that has components of all
-     * required types. All ComponentData's of the required types will be updated
-     * to access the components of the next entity. Optional ComponentData's
-     * will be updated to those components of the entity's, if they are
-     * attached. They may not be, in which case they will be flagged as invalid.
-     * </p>
-     * <p>
-     * It can be assumed that when an Entity is found that all required
-     * ComponentData's are valid and reference that entity's components of the
-     * appropriate type.
-     * </p>
-     * <p>
-     * True is returned if an Entity was found. False is returned if there were
-     * no more entities matching the requirements in the system. Additionally,
-     * if there are no required ComponentData's, then false is always returned.
-     * The iterator must be constrained by at least one required type.
-     * </p>
-     * 
-     * @return True if another entity was found and the required components (and
-     *         any present optional components) have been updated to that entity
-     */
-    public boolean next() {
-        if (primary == null)
-            return false;
-        
-        boolean found;
-        int entity;
-        int component;
-        int count = primary.owner.getMaxComponentIndex();
-        while(index < count - 1) {
-            index++; // always increment one
-
-            found = true;
-            entity = primary.owner.getEntityIndex(index);
-            if (entity != 0) {
-                // we have a possible entity candidate
-                primary.setFast(index);
-                for (int i = 0; i < required.length; i++) {
-                    component = required[i].owner.getComponentIndex(entity);
-                    if (!required[i].setFast(component)) {
-                        found = false;
-                        break;
-                    }
-                }
-                
-                if (found) {
-                    // we have satisfied all required components,
-                    // so now set all optional requirements as well
-                    for (int i = 0; i < optional.length; i++) {
-                        component = optional[i].owner.getComponentIndex(entity);
-                        optional[i].setFast(component); // we don't care if this is valid or not
-                    }
-                    
-                    return true;
-                }
-            }
-        }
-        
-        return false;
-    }
-
-    /**
-     * Reset this ComponentIterator to the beginning of the system to perform
-     * another complete iteration.
-     */
-    public void reset() {
-        index = 0;
-    }
-}

src/main/java/com/googlecode/entreri/ComponentRepository.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.googlecode.entreri;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Comparator;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import com.googlecode.entreri.property.BooleanProperty;
-import com.googlecode.entreri.property.CompactAwareProperty;
-import com.googlecode.entreri.property.IndexedDataStore;
-import com.googlecode.entreri.property.IntProperty;
-import com.googlecode.entreri.property.Property;
-import com.googlecode.entreri.property.PropertyFactory;
-
-/**
- * ComponentRepository manages storing all the componentDatas of a specific type for an
- * EntitySystem. It also controls the IndexedDataStore's for the type's set of
- * properties. It is package-private because its details are low-level and
- * complex.
- * 
- * @author Michael Ludwig
- * @param <T> The type of component stored by the index
- */
-final class ComponentRepository<T extends ComponentData<T>> {
-    private final EntitySystem system;
-    private final TypeId<T> type;
-
-    private final ComponentDataFactory<T> factory;
-    
-    // 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 int componentInsert;
-    
-    private final List<PropertyStore<?>> declaredProperties;
-    private final List<PropertyStore<?>> decoratedProperties;
-    
-    private final BooleanProperty enabledProperty; // this is also contained in decoratedProperties
-    private final IntProperty componentIdProperty; // this is contained in decoratedProperties
-    private int idSeq;
-     
-    /**
-     * Create a ComponentRepository for the given system, that will store Components
-     * of the given type.
-     * 
-     * @param system The owning system
-     * @param type The type of component
-     * @throws NullPointerException if system or type are null
-     */
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public ComponentRepository(EntitySystem system, TypeId<T> type, ComponentDataFactory<T> factory) {
-        if (system == null || type == null)
-            throw new NullPointerException("Arguments cannot be null");
-        
-        this.system = system;
-        this.factory = factory;
-        this.type = type;
-        
-        Map<?, PropertyFactory<?>> propertyFactories = factory.getPropertyFactories();
-        
-        declaredProperties = new ArrayList<PropertyStore<?>>();
-        decoratedProperties = new ArrayList<PropertyStore<?>>(); // empty for now
-        for (Entry<?, PropertyFactory<?>> e: propertyFactories.entrySet()) {
-            PropertyStore store = new PropertyStore(e.getValue(), e.getKey());
-            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
-        
-        componentInsert = 1;
-        
-        // Make sure properties' stores hold enough space
-        resizePropertyStores(declaredProperties, 1);
-        
-        // decorate the component data with a boolean property to track enabled status
-        enabledProperty = decorate(BooleanProperty.factory(1, true));
-        componentIdProperty = decorate(IntProperty.factory(1)); // we'll not assign a default value, since we change the id each time
-        idSeq = 1; // start at 1, just like entity id sequences
-    }
-    
-    /**
-     * @return The type of component data stored by this component index
-     */
-    public TypeId<T> getTypeId() {
-        return type;
-    }
-
-    /**
-     * @return The upper bound (exclusive) for component index values
-     */
-    public int getMaxComponentIndex() {
-        return componentInsert;
-    }
-    
-    /**
-     * @return The owning EntitySystem
-     */
-    public EntitySystem getEntitySystem() {
-        return system;
-    }
-
-    /**
-     * Given the index of a Component (e.g. {@link Component#getIndex()}, return
-     * the index of an entity within the owning system. The returned entity
-     * index can be safely passed to {@link EntitySystem#getEntityByIndex(int)}.
-     * 
-     * @param componentIndex The component index whose owning entity is fetched
-     * @return The index of the entity that has the given component index, or 0
-     *         if the component is not attached
-     */
-    public int getEntityIndex(int componentIndex) {
-        if (componentIndex >= componentIndexToEntityIndex.length) {
-            System.out.println("problem!");
-        }
-        return componentIndexToEntityIndex[componentIndex];
-    }
-
-    /**
-     * Given the index of an entity (e.g. {@link Entity#index}), return the
-     * index of the attached component of this ComponentRepository's type. The
-     * returned component index can be used in {@link #getComponent(int)} and
-     * related methods.
-     * 
-     * @param entityIndex The entity index to look up
-     * @return The index of the attached component, or 0 if the entity does not
-     *         have a component of this type attached
-     */
-    public int getComponentIndex(int entityIndex) {
-        return entityIndexToComponentRepository[entityIndex];
-    }
-
-    /**
-     * Ensure that this ComponentRepository has enough internal space to hold its
-     * entity-to-component mapping for the given number of entities.
-     * 
-     * @param numEntities The new number of entities
-     */
-    public void expandEntityIndex(int numEntities) {
-        if (entityIndexToComponentRepository.length < numEntities) {
-            entityIndexToComponentRepository = Arrays.copyOf(entityIndexToComponentRepository, (int) (numEntities * 1.5f) + 1);
-        }
-    }
-
-    /**
-     * @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, 0);
-    }
-
-    /**
-     * Set whether or not the component at <tt>componentIndex</tt> is enabled.
-     * 
-     * @param componentIndex The component index
-     * @param enabled True if the component is enabled
-     */
-    public void setEnabled(int componentIndex, boolean enabled) {
-        enabledProperty.set(enabled, componentIndex, 0);
-    }
-    
-    /**
-     * @param componentIndex The component index
-     * @return The component id of the component at the given index
-     */
-    public int getId(int componentIndex) {
-        return componentIdProperty.get(componentIndex, 0);
-    }
-    
-    /*
-     * As expandEntityIndex() but expands all related component data and arrays
-     * to hold the number of components.
-     */
-    private void expandComponentRepository(int numComponents) {
-        if (numComponents < components.length)
-            return;
-
-        int size = (int) (numComponents * 1.5f) + 1;
-        
-        // Expand the indexed data stores for the properties
-        resizePropertyStores(declaredProperties, size);
-        resizePropertyStores(decoratedProperties, size);
-        
-        // Expand the canonical component array
-        components = Arrays.copyOf(components, size);
-        
-        // Expand the component index
-        componentIndexToEntityIndex = Arrays.copyOf(componentIndexToEntityIndex, size);
-    }
-
-    /*
-     * Convenience to create a new data store for each property with the given
-     * size, copy the old data over, and assign it back to the property.
-     */
-    private void resizePropertyStores(List<PropertyStore<?>> properties, int size) {
-        int ct = properties.size();
-        for (int i = 0; i < ct; i++) {
-            IndexedDataStore oldStore = properties.get(i).property.getDataStore();
-            IndexedDataStore newStore = oldStore.create(size);
-            oldStore.copy(0, Math.min(oldStore.size(), size), newStore, 0);
-            properties.get(i).property.setDataStore(newStore);
-        }
-    }
-    
-    /**
-     * @param componentIndex The component index whose component is fetched
-     * @return The component reference at the given index, may be null
-     */
-    public Component<T> getComponent(int componentIndex) {
-        return components[componentIndex];
-    }
-
-    /**
-     * Create a new component and attach to it the entity at the given entity
-     * index, the new component will have its values copied from the existing
-     * template.
-     * 
-     * @param entityIndex The entity index which the component is attached to
-     * @param fromTemplate A template to assign values to the new component
-     * @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.getTypeId().equals(type))
-            throw new IllegalArgumentException("Component not of expected type, expected: " + type + ", but was: " + type);
-        if (!fromTemplate.isLive())
-            throw new IllegalStateException("Template component is not live");
-        
-        Component<T> instance = addComponent(entityIndex);
-        for (int i = 0; i < declaredProperties.size(); i++) {
-            PropertyStore store = declaredProperties.get(i);
-            store.clone(fromTemplate.index, store.property, instance.index);
-        }
-        
-        // fire add-event listener after cloning is completed
-        system.getControllerManager().fireComponentAdd(instance);
-        return instance;
-    }
-
-    /**
-     * Create a new component and attach to it the entity at the given entity
-     * index. The component will have the default state as specified by its
-     * properties.
-     * 
-     * @param entityIndex The entity index which the component is attached to
-     * @param initParams The var-args of parameters that must match the
-     *            InitParams annotation of the type
-     * @return A new component of type T
-     * @throws IllegalArgumentException if initParams is incorrect
-     */
-    public Component<T> addComponent(int entityIndex) {
-        Component<T> instance = allocateComponent(entityIndex);
-        
-        // fire add-event listener after initialization is completed
-        system.getControllerManager().fireComponentAdd(instance);
-        return instance;
-    }
-    
-    /*
-     * Allocate and store a new component, but don't initialize it yet.
-     */
-    private Component<T> allocateComponent(int entityIndex) {
-        if (entityIndexToComponentRepository[entityIndex] != 0)
-            removeComponent(entityIndex);
-        
-        int componentIndex = componentInsert++;
-        if (componentIndex >= components.length)
-            expandComponentRepository(componentIndex + 1);
-
-        Component<T> instance = new Component<T>(this, componentIndex);
-        components[componentIndex] = instance;
-        componentIndexToEntityIndex[componentIndex] = entityIndex;
-        entityIndexToComponentRepository[entityIndex] = componentIndex;
-
-        // Set default value for declared and decorated properties,
-        // this is needed because we might be overwriting a previously removed
-        // component, or the factory might be doing something tricky
-        for (int i = 0; i < declaredProperties.size(); i++) {
-            declaredProperties.get(i).setValue(componentIndex);
-        }
-        
-        for (int i = 0; i < decoratedProperties.size(); i++) {
-            decoratedProperties.get(i).setValue(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, 0);
-        
-        return instance;
-    }
-
-    /**
-     * Create a new ComponentData of type T that can be used to view components
-     * in this index.
-     * 
-     * @return A new data instance
-     */
-    public T createDataInstance() {
-        // create a new instance from the factory - it will be completely detached
-        T t = factory.createInstance();
-        
-        // 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++) {
-            PropertyStore<?> p = declaredProperties.get(i);
-            factory.setProperty(t, p.key, p.property);
-        }
-        
-        t.set(null);
-        return t;
-    }
-
-    /**
-     * Detach or remove any component of this index's type from the entity with
-     * the given index. True is returned if a component was removed, or false
-     * otherwise.
-     * 
-     * @param entityIndex The entity's index whose component is removed
-     * @return True if a component was removed
-     */
-    public boolean removeComponent(int entityIndex) {
-        int componentIndex = entityIndexToComponentRepository[entityIndex];
-
-        // This code works even if componentIndex is 0
-        Component<T> oldComponent = components[componentIndex];
-        if (oldComponent != null) {
-            // perform component clean up before data is invalidated
-            system.getControllerManager().fireComponentRemove(oldComponent);
-            oldComponent.index = 0;
-        }
-
-        components[componentIndex] = null;
-        entityIndexToComponentRepository[entityIndex] = 0; // entity does not have component
-        componentIndexToEntityIndex[componentIndex] = 0; // component does not have entity
-        componentIdProperty.set(0, componentIndex, 0);
-        enabledProperty.set(false, componentIndex, 0);
-        
-        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<PropertyStore<?>> properties, Component<T>[] newToOldMap) {
-        for (int i = 0; i < properties.size(); i++) {
-            PropertyStore<?> p = properties.get(i);
-            IndexedDataStore origStore = p.property.getDataStore();
-            
-            p.property.setDataStore(update(origStore, p.swap, newToOldMap));
-            p.swap = origStore;
-        }
-    }
-
-    /*
-     * Update all component data in src to be in dst by shuffling it to match
-     * newToOldMap.
-     */
-    private IndexedDataStore update(IndexedDataStore src, IndexedDataStore dst, 
-                                    Component<T>[] newToOldMap) {
-        int dstSize = newToOldMap.length;
-        
-        if (dst == null || dst.size() < dstSize)
-            dst = src.create(dstSize);
-        
-        int i;
-        int lastIndex = -1;
-        int copyIndexNew = -1;
-        int copyIndexOld = -1;
-        for (i = 1; i < componentInsert; i++) {
-            if (newToOldMap[i] == null) {
-                // we've hit the end of existing componentDatas, so break
-                break;
-            }
-            
-            if (newToOldMap[i].index != 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 = newToOldMap[i].index;
-            }
-            lastIndex = newToOldMap[i].index;
-        }
-        
-        if (copyIndexOld >= 0) {
-            // final copy
-            src.copy(copyIndexOld, (i - copyIndexNew), dst, copyIndexNew);
-        }
-
-        return dst;
-    }
-    
-    private void notifyCompactAwareProperties(List<PropertyStore<?>> props) {
-        PropertyStore<?> p;
-        for (int i = 0; i < props.size(); i++) {
-            p = props.get(i);
-            if (p.property instanceof CompactAwareProperty)
-                ((CompactAwareProperty) p.property).onCompactComplete();
-        }
-    }
-
-    /**
-     * <p>
-     * Compact the data of this ComponentRepository to account for removals and
-     * additions to the index. This will ensure that all active componentDatas are
-     * packed into the underlying arrays, and that they will be accessed in the
-     * same order as iterating over the entities directly.
-     * </p>
-     * <p>
-     * The map from old to new entity index must be used to properly update the
-     * component index's data so that the system is kept in sync.
-     * </p>
-     * 
-     * @param entityOldToNewMap A map from old entity index to new index
-     * @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>>() {
-            @Override
-            public int compare(Component<T> o1, Component<T> o2) {
-                if (o1 != null && o2 != null)
-                    return componentIndexToEntityIndex[o1.index] - componentIndexToEntityIndex[o2.index];
-                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"
-            }
-        });
-        
-        // Update all of the property stores to match up with the componentDatas new positions
-        update(declaredProperties, components);
-        update(decoratedProperties, components);
-        
-        // 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;
-                componentInsert = i + 1;
-            }
-        }
-        componentIndexToEntityIndex = newComponentRepository;
-        
-        // Possibly compact the component data
-        if (componentInsert < .6f * components.length) {
-            int newSize = (int) (1.2f * componentInsert) + 1;
-            components = Arrays.copyOf(components, newSize);
-            componentIndexToEntityIndex = Arrays.copyOf(componentIndexToEntityIndex, newSize);
-            resizePropertyStores(declaredProperties, newSize);
-            resizePropertyStores(decoratedProperties, newSize);
-        }
-        
-        // Repair entityIndexToComponentRepository - and possible shrink the index
-        // based on the number of packed entities
-        if (numEntities < .6f * entityIndexToComponentRepository.length)
-            entityIndexToComponentRepository = new int[(int) (1.2f * numEntities) + 1];
-        else
-            Arrays.fill(entityIndexToComponentRepository, 0);
-        
-        for (int i = 1; i < componentInsert; i++)
-            entityIndexToComponentRepository[componentIndexToEntityIndex[i]] = i;
-        
-        notifyCompactAwareProperties(declaredProperties);
-        notifyCompactAwareProperties(decoratedProperties);
-    }
-
-    /**
-     * Decorate the type information of this ComponentRepository to add a property
-     * created by the given factory. The returned property will have default
-     * data assigned for each current Component in the index, and will have the
-     * default value assigned for each new Component. Decorators can then access
-     * the returned property to manipulate the decorated component data.
-     * 
-     * @param <P> The type of property created
-     * @param factory The factory that will create a unique Property instance
-     *            associated with the decorated property and this index
-     * @return The property decorated onto the type of the index
-     */
-    public <P extends Property> P decorate(PropertyFactory<P> factory) {
-        int size = (declaredProperties.isEmpty() ? componentInsert
-                                                 : declaredProperties.get(0).property.getDataStore().size());
-        
-        PropertyStore<P> pstore = new PropertyStore<P>(factory, "decorated");
-
-        // Set values from factory to all component slots
-        IndexedDataStore newStore = pstore.property.getDataStore().create(size);
-        pstore.property.setDataStore(newStore);
-        for (int i = 1; i < size; i++) {
-            pstore.setValue(i);
-        }
-        
-        decoratedProperties.add(pstore);
-        return pstore.property;
-    }
-
-    /**
-     * Remove the given property from the set of decorated properties on this
-     * index's type. If the property is invalid or not a decorated property for
-     * the index, this does nothing.
-     * 
-     * @param p The property to remove
-     */
-    public void undecorate(Property p) {
-        Iterator<PropertyStore<?>> it = decoratedProperties.iterator();
-        while(it.hasNext()) {
-            if (it.next().property == p) {
-                it.remove();
-                break;
-            }
-        }
-    }
-    
-    /*
-     * Type wrapping a key, property, and factory, as well as an auxiliary data
-     * store for compaction.
-     */
-    private static class PropertyStore<P extends Property> {
-        final Object key;
-        final P property;
-        final PropertyFactory<P> creator;
-        IndexedDataStore swap; // may be null
-        
-        public PropertyStore(PropertyFactory<P> creator, Object key) {
-            this.creator = creator;
-            this.key = key;
-            property = creator.create();
-        }
-        
-        private void clone(int srcIndex, P dst, int dstIndex) {
-            creator.clone(property, srcIndex, dst, dstIndex);
-        }
-        
-        private void setValue(int index) {
-            creator.setDefaultValue(property, index);
-        }
-    }
-}

src/main/java/com/googlecode/entreri/Controller.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.googlecode.entreri;
-
-/**
- * <p>
- * Controllers are functional processors of the entities and components within
- * an EntitySystem. Different Controller implementations have different
- * purposes, such as updating transforms, computing physics or AI, or rendering
- * a scene. Generally controller implementations should be as small and as
- * independent as possible to allow for reuse and easy composability.
- * </p>
- * <p>
- * Controllers also have event listener method hooks so that they can be
- * notified when they are added or removed from an EntitySystem, or when an
- * Entity or component in a system they're attached to is added or removed.
- * </p>
- * <p>
- * Controller instances should only be added to a single EntitySystem at a time.
- * If a controller is later removed, it can safely be added to a new
- * EntitySystem. Controller implementations are responsible for ensuring this in
- * their {@link #init(EntitySystem)} and {@link #destroy()} methods. It is
- * recommended to extend {@link SimpleController} because it handles the
- * necessary logic.
- * </p>
- * 
- * @author Michael Ludwig
- */
-public interface Controller {
-    /**
-     * Invoke operations on the controller's EntitySystem that must occur before
-     * the main processing. All controllers in a system will have their
-     * preProcess() method called before the first process() method is called.
-     * 
-     * @param dt The elapsed time since the last processing
-     */
-    public void preProcess(float dt);
-
-    /**
-     * Invoke controller specific operations to process the EntitySystem. All
-     * controllers will have already had their preProcess() method invoked and
-     * none will have postProcess() invoked until process() has completed for
-     * all controllers in the system.
-     * 
-     * @param dt The elapsed time since the last processing
-     */
-    public void process(float dt);
-
-    /**
-     * Invoked at the end of a processing phase after all controllers in a
-     * system have completed their process() methods.
-     * 
-     * @param dt The elapsed time since the last processing
-     */
-    public void postProcess(float dt);
-
-    /**
-     * Invoked when a Controller is added to an EntitySystem. This method
-     * should initialize any system-specific state used by the controller,
-     * such as ComponentIterators, ComponentData instances, or decorated
-     * properties.
-     * 
-     * @param system The new EntitySystem this controller is attached to
-     * @throws IllegalStateException if the controller is already attached to
-     *             another entity system
-     */
-    public void init(EntitySystem system);
-
-    /**
-     * Invoked when a Controller is detached from its EntitySystem. This should
-     * clean up any decorated properties added to the system. It should also
-     * update its internal state to make it safe to call
-     * {@link #init(EntitySystem)} later.
-     */
-    public void destroy();
-
-    /**
-     * Invoked when an Entity is added to the Controller's EntitySystem. If the
-     * Entity was added with the {@link EntitySystem#addEntity(Entity)} method,
-     * this is invoked before template's components have been copied to the
-     * entity.
-     * 
-     * @param e The new entity
-     */
-    public void onEntityAdd(Entity e);
-
-    /**
-     * <p>
-     * Invoked when an Entity is removed from the Controller's EntitySystem. The
-     * Entity is still alive when this is called, but will be removed completely
-     * after all Controllers have processed the remove.
-     * </p>
-     * <p>
-     * When an entity is removed, all of its components are removed first, so
-     * {@link #onComponentRemove(Component)} will be invoked on all attached
-     * components before this listener method is caled.
-     * </p>
-     * 
-     * @param e The entity being removed
-     */
-    public void onEntityRemove(Entity e);
-
-    /**
-     * Invoked when the given Component is added to an Entity. The component is
-     * alive so its Entity can be queried in the usual manner.
-     * 
-     * @param c The new component
-     */
-    public void onComponentAdd(Component<?> c);
-
-    /**
-     * Invoked when the given Component is about to be removed. At the time this
-     * is invoked, the Component is still alive. This may be invoked in response
-     * to removing the Component from the entity, or if the component's owning
-     * entity is removed.
-     * 
-     * @param c The component being removed
-     */
-    public void onComponentRemove(Component<?> c);
-
-    /**
-     * @return The current EntitySystem processed by this controller, or null if
-     *         it is not added to any EntitySystem
-     */
-    public EntitySystem getEntitySystem();
-}

src/main/java/com/googlecode/entreri/ControllerManager.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.googlecode.entreri;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-
-/**
- * <p>
- * ControllerManager is a utility that manages the list of Controllers that can
- * process an EntitySystem. It also provides a mechanism to share data between
- * controllers by storing objects associated with {@link Key Keys}. If a
- * Controller type publically exposes a static key, other controllers can then
- * look up the associated value.
- * </p>
- * <p>
- * Additionally, the ControllerManager is used to invoke the phase processing
- * methods on each Controller on its configured EntitySystem.
- * </p>
- * 
- * @author Michael Ludwig
- */
-public class ControllerManager {
-    /**
-     * Key represents a typed key into controller data managed by a
-     * ControllerManager. Equality is defined by reference, so it is generally
-     * best to store keys for reuse in private or public fields (possibly static
-     * if the key is shared).
-     * 
-     * @param <T> The type of data associated with the key
-     */
-    public static class Key<T> { }
-    
-    /**
-     * The Phase enum represents the different phases of
-     * processing that an EntitySystem can go through during
-     * what is often considered a "frame".
-     */
-    public static enum Phase {
-        /**
-         * The PREPROCESS phase is invoked before all other phases. All
-         * controllers in a manager will have their
-         * {@link Controller#preProcess(float)} method called
-         * before moving to the next phase.
-         */
-        PREPROCESS,
-
-        /**
-         * The PROCESS phase is invoked between PREPROCESS and POSTPROCESS. All
-         * controllers in the manager will have their
-         * {@link Controller#process(float)} method called before
-         * moving to the next phase.
-         */
-        PROCESS,
-
-        /**
-         * The POSTPROCESS phase is invoked after PREPROCESS and POSTPROCESS.
-         * All controllers in their manager will have their
-         * {@link Controller#postProcess(float)} method called
-         * before the frame is completed.
-         */
-        POSTPROCESS,
-
-        /**
-         * ALL is a special PHASE that represents all three true phases invoked
-         * in their proper order.
-         */
-        ALL
-    }
-
-    private float fixedDelta;
-    private final List<Controller> controllers;
-    
-    // This is a concurrent map so that parallel controllers can access it efficiently
-    // - the rest of the class is assumed to be single-threaded
-    private final ConcurrentHashMap<Key<?>, Object> controllerData;
-
-    private final EntitySystem system;
-
-    /**
-     * Create a new ControllerManager that will store controllers and controller
-     * data for processing the given EntitySystem.
-     * 
-     * @param system The EntitySystem that is managed by this controller manager
-     * @throws NullPointerException if system is null
-     */
-    public ControllerManager(EntitySystem system) {
-        if (system == null)
-            throw new NullPointerException("EntitySystem cannot be null");
-        
-        this.system = system;
-        controllerData = new ConcurrentHashMap<Key<?>, Object>();
-        controllers = new ArrayList<Controller>();
-        
-        fixedDelta = 1 / 60f; // 60fps
-    }
-
-    /**
-     * Set a new fixed time frame delta. This can take any value and represents
-     * the number of seconds between each frame, so negative values may produce
-     * undefined results with some controllers. This value is only used if
-     * {@link #process()} is invoked, the other varieties take a delta value
-     * that overrides this one.
-     * 
-     * @param dt The new time frame delta
-     */
-    public void setFixedDelta(float dt) {
-        fixedDelta = dt;
-    }
-
-    /**
-     * @return The current fixed time frame delta that is used if
-     *         {@link #process()} is invoked
-     */
-    public float getFixedDelta() {
-        return fixedDelta;
-    }
-
-    /**
-     * Return the controller data that has been mapped to the given <tt>key</tt>
-     * . This will return null if there has been no assigned data. The getData()
-     * and setData() methods should be used by controllers to share data between
-     * themselves, and to store system-dependent state so that their
-     * implementations are properly indepdent of the system.
-     * 
-     * @param key The annotation key
-     * @return The object previously mapped to the annotation with
-     *         {@link #setData(Key, Object)}
-     * @throws NullPointerException if key is null
-     */
-    @SuppressWarnings("unchecked")
-    public <T> T getData(Key<T> key) {
-        if (key == null)
-            throw new NullPointerException("Key cannot be null");
-        return (T) controllerData.get(key);
-    }
-
-    /**
-     * Store <tt>value</tt> to the given <tt>key</tt> so that future
-     * calls to {@link #getData(Key)} with the same key will return
-     * the new value. If the value is null, any previous mapping is removed.
-     * 
-     * @param key The key
-     * @param value The new value to store
-     * @throws NullPointerException if key is null
-     */
-    public <T> void setData(Key<T> key, T value) {
-        if (key == null)
-            throw new NullPointerException("Key cannot be null");
-        if (value == null)
-            controllerData.remove(key);
-        else
-            controllerData.put(key, value);
-    }
-
-    /**
-     * <p>
-     * Add a Controller to this manager so that subsequent calls to
-     * {@link #process()} and its varieties will invoke the process hooks on the
-     * controller. The new controller is invoked after all already added
-     * controllers.
-     * </p>
-     * <p>
-     * If this controller was already added to the manager, it will be moved to
-     * the end of the list.
-     * </p>
-     * 
-     * @param controller The controller to add
-     * @throws NullPointerException if controller is null
-     */
-    public void addController(Controller controller) {
-        if (controller == null)
-            throw new NullPointerException("Controller cannot be null");
-        
-        // remove it first - which does nothing if not in the list
-        boolean removed = controllers.remove(controller);
-        // now add it to the end
-        controllers.add(controller);
-        
-        if (!removed)
-            controller.init(system);
-    }
-
-    /**
-     * Remove a controller from the manager so that it is no longer invoked when
-     * {@link #process()} and its related functions are called. If the
-     * controller has not been added to the manager, this does nothing.
-     * 
-     * @param controller The controller to remove
-     * @throws NullPointerException if controller is null
-     */
-    public void removeController(Controller controller) {
-        if (controller == null)
-            throw new NullPointerException("Controller cannot be null");
-        boolean removed = controllers.remove(controller);
-        if (removed)
-            controller.destroy();
-    }
-    
-    /**
-     * Run all phases of the manager using the current fixed frame time delta.
-     */
-    public void process() {
-        process(fixedDelta);
-    }
-
-    /**
-     * Run all phases of the manager using the specified frame time delta,
-     * <tt>dt</tt>.
-     * 
-     * @param dt The time delta for the frame, or the amount of time since the