View Javadoc

1   /*
2    * Entreri, an entity-component framework in Java
3    *
4    * Copyright (c) 2013, Michael Ludwig
5    * All rights reserved.
6    *
7    * Redistribution and use in source and binary forms, with or without modification,
8    * are permitted provided that the following conditions are met:
9    *
10   *     Redistributions of source code must retain the above copyright notice,
11   *         this list of conditions and the following disclaimer.
12   *     Redistributions in binary form must reproduce the above copyright notice,
13   *         this list of conditions and the following disclaimer in the
14   *         documentation and/or other materials provided with the distribution.
15   *
16   * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17   * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18   * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19   * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
20   * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21   * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22   * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23   * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24   * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25   * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26   */
27  package com.lhkbob.entreri.impl;
28  
29  import com.lhkbob.entreri.*;
30  
31  import java.util.Iterator;
32  import java.util.NoSuchElementException;
33  
34  /**
35   * Entity implementation used by EntitySystemImpl.
36   *
37   * @author Michael Ludwig
38   */
39  public final class EntityImpl implements Entity {
40      private final EntitySystemImpl system;
41      private final int id;
42  
43      final OwnerSupport delegate;
44  
45      int index;
46  
47      /**
48       * Create an Entity that will be owned by the given system and is placed at the given
49       * index.
50       *
51       * @param system The owning system
52       * @param index  The index into the system
53       * @param id     The unique id of the entity in the system
54       */
55      EntityImpl(EntitySystemImpl system, int index, int id) {
56          if (system == null) {
57              throw new NullPointerException("System cannot be null");
58          }
59          if (index < 0) {
60              throw new IllegalArgumentException("Index must be at least 0, not: " + index);
61          }
62  
63          this.system = system;
64          this.index = index;
65          this.id = id;
66  
67          delegate = new OwnerSupport(this);
68      }
69  
70      @Override
71      public int getId() {
72          return id;
73      }
74  
75      @Override
76      public EntitySystem getEntitySystem() {
77          return system;
78      }
79  
80      @Override
81      public boolean isAlive() {
82          return index != 0;
83      }
84  
85      @Override
86      public <T extends Component> T get(Class<T> componentType) {
87          ComponentRepository<T> ci = system.getRepository(componentType);
88          return ci.getComponent(ci.getComponentIndex(index));
89      }
90  
91      @Override
92      public <T extends Component> T add(Class<T> componentType) {
93          ComponentRepository<T> ci = system.getRepository(componentType);
94          return ci.addComponent(index);
95      }
96  
97      @Override
98      public <T extends Component> T as(Class<T> componentType) {
99          ComponentRepository<T> ci = system.getRepository(componentType);
100         int componentIndex = ci.getComponentIndex(index);
101         if (componentIndex > 0) {
102             return ci.getComponent(componentIndex);
103         } else {
104             return ci.addComponent(index);
105         }
106     }
107 
108     @Override
109     public boolean has(Class<? extends Component> componentType) {
110         ComponentRepository<?> ci = system.getRepository(componentType);
111         return ci.getComponentIndex(index) > 0;
112     }
113 
114     @Override
115     @SuppressWarnings({ "unchecked", "rawtypes" })
116     public <T extends Component> T add(T toClone) {
117         if (toClone == null) {
118             throw new NullPointerException(
119                     "ComponentData template, toClone, cannot be null");
120         }
121         ComponentRepository ci = system.getRepository(toClone.getType());
122         return (T) ci.addComponent(index, toClone);
123     }
124 
125     @Override
126     public boolean remove(Class<? extends Component> componentType) {
127         ComponentRepository<?> ci = system.getRepository(componentType);
128         return ci.removeComponent(index);
129     }
130 
131     @Override
132     public Iterator<Component> iterator() {
133         return new ComponentIterator(system, index);
134     }
135 
136     /*
137      * Iterator implementation that iterates over the components attached to an
138      * entity, based on entity index rather than reference
139      */
140     private static class ComponentIterator implements Iterator<Component> {
141         private final int entityIndex;
142         private final Iterator<ComponentRepository<?>> indices;
143 
144         private ComponentRepository<?> currentIndex;
145         private ComponentRepository<?> nextIndex;
146 
147         public ComponentIterator(EntitySystemImpl system, int entityIndex) {
148             this.entityIndex = entityIndex;
149             indices = system.indexIterator();
150         }
151 
152         @Override
153         public boolean hasNext() {
154             if (nextIndex == null) {
155                 advance();
156             }
157             return nextIndex != null;
158         }
159 
160         @Override
161         public Component next() {
162             if (!hasNext()) {
163                 throw new NoSuchElementException();
164             }
165 
166             currentIndex = nextIndex;
167             nextIndex = null;
168             return currentIndex.getComponent(currentIndex.getComponentIndex(entityIndex));
169         }
170 
171         @Override
172         public void remove() {
173             if (currentIndex == null) {
174                 throw new IllegalStateException("Must call next first");
175             }
176 
177             if (currentIndex.removeComponent(entityIndex)) {
178                 currentIndex = null; // so next call to remove() fails
179             } else {
180                 throw new IllegalStateException("Already removed");
181             }
182         }
183 
184         private void advance() {
185             while (indices.hasNext()) {
186                 nextIndex = indices.next();
187 
188                 int index = nextIndex.getComponentIndex(entityIndex);
189                 if (index != 0) {
190                     break;
191                 } else {
192                     nextIndex = null; // must set to null if this was last element
193                 }
194             }
195         }
196     }
197 
198     @Override
199     public int compareTo(Entity o) {
200         return id - o.getId();
201     }
202 
203     @Override
204     public int hashCode() {
205         return id;
206     }
207 
208     @Override
209     public boolean equals(Object o) {
210         return o instanceof EntityImpl && o == this;
211     }
212 
213     @Override
214     public Owner notifyOwnershipGranted(Ownable obj) {
215         delegate.notifyOwnershipGranted(obj);
216         return this;
217     }
218 
219     @Override
220     public void notifyOwnershipRevoked(Ownable obj) {
221         delegate.notifyOwnershipRevoked(obj);
222     }
223 
224     @Override
225     public void setOwner(Owner owner) {
226         delegate.setOwner(owner);
227     }
228 
229     @Override
230     public Owner getOwner() {
231         return delegate.getOwner();
232     }
233 
234     @Override
235     public String toString() {
236         StringBuilder sb = new StringBuilder("Entity(");
237         sb.append(id);
238 
239         Iterator<Component> it = iterator();
240         while (it.hasNext()) {
241             sb.append(",\n\t").append(it.next());
242         }
243 
244         sb.append(")");
245         return sb.toString();
246     }
247 }