package org.terasology.entitySystem.pojo; import com.google.common.collect.Lists; import com.google.common.collect.Maps; import org.terasology.entitySystem.AbstractPrefab; import org.terasology.entitySystem.Component; import org.terasology.entitySystem.Prefab; import org.terasology.entitySystem.metadata.ComponentLibrary; import java.util.Collections; import java.util.List; import java.util.Map; /** * @author Immortius <immortius@gmail.com> */ public class PojoPrefab extends AbstractPrefab implements Prefab { private ComponentLibrary componentLibrary; private Map<Class<? extends Component>, Component> components; private List<Prefab> parents; private transient Map<Class<? extends Component>, Component> componentCache; protected PojoPrefab(String name, ComponentLibrary componentLibrary) { this(name, componentLibrary, Maps.<Class<? extends Component>, Component>newHashMap(), Lists.<Prefab>newLinkedList()); } protected PojoPrefab(String name, ComponentLibrary componentLibrary, Map<Class<? extends Component>, Component> components, List<Prefab> parents) { super(name); this.componentLibrary = componentLibrary; this.components = components; this.parents = parents; } public <T extends Component> T getComponent(Class<T> componentClass) { checkComponentCache(); return componentClass.cast(componentCache.get(componentClass)); } public <T extends Component> T setComponent(T component) { // Update data this.components.put(component.getClass(), component); if (componentCache != null) { this.componentCache.put(component.getClass(), component); } return component; } public void removeComponent(Class<? extends Component> componentClass) { components.remove(componentClass); this.invalidateComponentCache(); } public Iterable<Component> listComponents() { checkComponentCache(); return Collections.unmodifiableCollection(componentCache.values()); } public Iterable<Component> listOwnComponents() { return Collections.unmodifiableCollection(components.values()); } public void addParent(Prefab parent) { if (this.parents.contains(parent)) { // @todo should we throw IllegalArgumentException instead? return; } this.parents.add(parent); this.invalidateComponentCache(); } public Iterable<Prefab> getParents() { return Collections.unmodifiableCollection(parents); } public void removeParent(Prefab parent) { this.parents.remove(parent); invalidateComponentCache(); } private void checkComponentCache() { if (components == null) { throw new IllegalStateException("Prefab is destroyed!"); } if (componentCache == null) { this.buildComponentCache(); } } private void invalidateComponentCache() { componentCache = null; } private void buildComponentCache() { // save old cache, will find changes from it Map<Class<? extends Component>, Component> oldCache = componentCache; componentCache = Maps.newHashMap(); // 1) Fill inherited components for (Prefab ref : this.getParents()) { for (Component component : ref.listComponents()) { componentCache.put(component.getClass(), componentLibrary.copy(component)); } } // 2) Find delta changes if (oldCache != null) { for (Component component : oldCache.values()) { if (!component.equals(componentCache.get(component.getClass()))) { components.put(component.getClass(), component); } } } // 3) Fill own components for (Component component : components.values()) { componentCache.put(component.getClass(), component); } // 4) PROFIT! } }