/* * Copyright (c) 2017 NOVA, All rights reserved. * This library is free software, licensed under GNU Lesser General Public License version 3 * * This file is part of NOVA. * * NOVA is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NOVA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NOVA. If not, see <http://www.gnu.org/licenses/>. */ package nova.core.component; import nova.core.component.exception.ComponentException; import nova.core.util.Direction; import nova.internal.core.Game; import se.jbee.inject.Dependency; import java.util.Arrays; import java.util.HashSet; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.function.Function; import java.util.stream.Collectors; /** * A class that contains all sided components. * @author ExE Boss */ public class SidedComponentMap extends ComponentMap { private static final long serialVersionUID = 2017_02_12L; public final ComponentMap up = new ComponentMap(provider); public final ComponentMap down = new ComponentMap(provider); public final ComponentMap north = new ComponentMap(provider); public final ComponentMap south = new ComponentMap(provider); public final ComponentMap west = new ComponentMap(provider); public final ComponentMap east = new ComponentMap(provider); public SidedComponentMap(ComponentProvider<?> provider) { super(provider); } public final Map<Direction, ComponentMap> getComponentsForDirections() { return Arrays.stream(Direction.values()).collect(Collectors.toMap(Function.identity(), this::getComponents)); } public final ComponentMap getComponents(Direction direction) { switch (direction) { case UP: return up; case DOWN: return down; case NORTH: return north; case SOUTH: return south; case WEST: return west; case EAST: return east; default: return this; } } /** * Adds a new component based on its superclass or interface using dependency injection. * @param direction The direction to add the component to. * @param componentType The interface or abstract class associated with the new component. * @param <C> The node type. * @return A new node of N type. */ public final <C extends Component> C add(Class<C> componentType, Direction direction) { return getComponents(direction).add(ensureValid(componentType, direction)); } /** * Adds a new component based on its superclass or interface using dependency injection. * @param directions The directions to add the component to. * @param componentType The interface or abstract class associated with the new component. * @param <C> The node type. * @return A new node of N type. */ public final <C extends Component> C add(Class<C> componentType, Direction... directions) { return this.add(Game.injector().resolve(Dependency.dependency(ensureValid(componentType, directions))), directions); } /** * Adds a component to the provider. * @param direction The direction to add the component to. * @param component The component to add. * @return the component. * @throws ComponentException when the component already exists on the block. */ public final <C extends Component> C add(C component, Direction direction) { return getComponents(direction).add(ensureValid(component, direction)); } /** * Adds a component to the provider. * @param component The component to add. * @param directions The directions to add the component to. * @return the component. * @throws ComponentException when the component already exists on the block. */ public final <C extends Component> C add(C component, Direction... directions) { for (Direction direction : directions) return getComponents(direction).add(ensureValid(component, direction)); return component; } /** * Adds a component to the block if it is not present. * @param component The component to add. * @param direction The direction to get or add the component to. * @return the component. */ public final <C extends Component> C getOrAdd(C component, Direction direction) { return getComponents(direction).getOrAdd(component); } /** * Checks if a component type exists in this provider. * @param componentType the component type to check. * @param direction The direction to check. * @return true if the component exists on the provider. */ public final boolean has(Class<?> componentType, Direction direction) { return getComponents(direction).has(componentType) || this.has(componentType); } /** * Checks if a component type can be removed from this provider. * @param componentType the component type to check. * @param direction The direction to check. * @return true if the component exists on the provider. */ public final boolean canRemove(Class<?> componentType, Direction direction) { return getComponents(direction).has(componentType); } /** * Removes the component from the block. * @param component the component type. * @param direction The direction to remove the component from. * @return the component removed. * @throws ComponentException when the component does not exist. */ public final <C extends Component> C remove(C component, Direction direction) { return getComponents(direction).remove(component); } /** * Removes the component from the provider. * @param componentType the component type. * @param direction The direction to remove the component from. * @return the component removed. * @throws ComponentException when the component does not exist. */ @SuppressWarnings("unchecked") public final <C extends Component> C remove(Class<C> componentType, Direction direction) { return getComponents(direction).remove(componentType); } /** * Gets an optional of the component with the specified type. * @param componentType the type to get. * @param direction The direction to get the component from. * @return the optional of the component found or {@code Optional.empty()}. * if the component was not found. */ @SuppressWarnings("unchecked") public final <C> Optional<C> getOp(Class<C> componentType, Direction direction) { Optional<C> c = getComponents(direction).getOp(componentType); if (!c.isPresent()) c = this.getOp(componentType); return c; } /** * Gets the component with the specified type. * @param componentType the type to get. * @param direction The direction to get the component from. * @return the component. * @throws ComponentException if the component doesn't exist. */ public final <C> C get(Class<C> componentType, Direction direction) { return getComponents(direction).getOp(componentType).orElseGet(() -> this.get(componentType)); } /** * Gets the set of the components with the specified type. * @param componentType the type to get. * @param direction The direction to get the component from. * @return the set of the components. */ public final <C> Set<C> getSet(Class<C> componentType, Direction direction) { Set<C> c = new HashSet<>(getComponents(direction).getSet(componentType)); c.addAll(this.getSet(componentType)); return c; } private <C extends Component> C ensureValid(C component, Direction... directions) { for (Direction direction : directions) ensureValid(component, direction); return component; } private <C extends Component> C ensureValid(C component, Direction direction) { ensureValid(component.getClass(), direction); return component; } private <C extends Component> Class<C> ensureValid(Class<C> componentType, Direction... directions) { for (Direction direction : directions) ensureValid(componentType, direction); return componentType; } private <C extends Component> Class<C> ensureValid(Class<C> componentType, Direction direction) { if (getComponents(direction) == this) return componentType; if (componentType.isAnnotationPresent(UnsidedComponent.class)) throw new IllegalArgumentException("Unsided components can't be added to a specific side"); return componentType; } }