/* * Copyright (C) 2012 Sebastian Straub <sebastian-straub@gmx.net> * * This program 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. * * This program 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 this program. If not, see <http://www.gnu.org/licenses/>. */ package de.nx42.wotcrawler.ext; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import de.nx42.wotcrawler.db.BaseProperties.Development; import de.nx42.wotcrawler.db.TanksDB; import de.nx42.wotcrawler.db.module.Engine; import de.nx42.wotcrawler.db.module.Gun; import de.nx42.wotcrawler.db.module.Module; import de.nx42.wotcrawler.db.module.Module.ModuleType; import de.nx42.wotcrawler.db.module.Radio; import de.nx42.wotcrawler.db.module.Suspension; import de.nx42.wotcrawler.db.module.Turret; import de.nx42.wotcrawler.db.tank.Tank; import de.nx42.wotcrawler.db.tank.TankRef; /** * The ModuleMap holds a Map for each module type that maps from each tank * to a list of compatible modules. The lists are static so they can be * accessed by anonymous inner classes like the enums of the Field class. * * The purpose of this class is to give fast access for any tank to it's modules. * The list of modules is ordered from worst to best, so the stock and top modules * can be accessed easily. * * @author Sebastian Straub <sebastian-straub@gmx.net> */ public class ModuleMap { private static final Logger log = LoggerFactory.getLogger(ModuleMap.class); /** Maps from a Tank to its compatible engines, ordered from worst to best */ public static Map<Tank, List<Engine>> engine = new HashMap<Tank, List<Engine>>(); /** Maps from a Tank to its compatible guns, ordered from worst to best */ public static Map<Tank, List<Gun>> gun = new HashMap<Tank, List<Gun>>(); /** Maps from a Tank to its compatible radios, ordered from worst to best */ public static Map<Tank, List<Radio>> radio = new HashMap<Tank, List<Radio>>(); /** Maps from a Tank to its compatible suspensions, ordered from worst to best */ public static Map<Tank, List<Suspension>> suspension = new HashMap<Tank, List<Suspension>>(); /** Maps from a Tank to its compatible turrets, ordered from worst to best */ public static Map<Tank, List<Turret>> turret = new HashMap<Tank, List<Turret>>(); /** * Builds the module map for each tank and module from the given TanksDB * @param db the database to build the maps from * @return the filled ModuleMap */ public static ModuleMap build(TanksDB db) { ModuleMap mm = new ModuleMap(); mm.buildModuleMapping(db); return mm; } /** * Builds the Module mapping for all tanks. * Fills the static Maps that map from each tank to it's list of Modules. * The maps are sorted in ascending order, so the worst module is at the * first position, the best at last. * The maps are heavily used by the enum specific functions, so they need * to be static... */ public final void buildModuleMapping(TanksDB db) { // tanks init for (Tank t : db.tanks) { engine.put(t, new ArrayList<Engine>(2)); gun.put(t, new ArrayList<Gun>(2)); radio.put(t, new ArrayList<Radio>(2)); suspension.put(t, new ArrayList<Suspension>(2)); turret.put(t, new ArrayList<Turret>(2)); } // engines for (Engine e : db.modules.engines) { for(TankRef t : e.compatibility) { if(t.ref != null) { engine.get(t.ref).add(e); } } } // guns for (Gun g : db.modules.guns) { for(TankRef t : g.compatibility) { if(t.ref != null) { gun.get(t.ref).add(g); } } } // radios for (Radio r : db.modules.radios) { for(TankRef t : r.compatibility) { if(t.ref != null) { radio.get(t.ref).add(r); } } } // suspensions for (Suspension s : db.modules.suspensions) { for(TankRef t : s.compatibility) { if(t.ref != null) { suspension.get(t.ref).add(s); } } } // turrets for (Turret tr : db.modules.turrets) { for(TankRef t : tr.compatibility) { if(t.ref != null) { turret.get(t.ref).add(tr); } } } // sort for (List<Engine> eMaps : engine.values()) { Collections.sort(eMaps); } for (List<Gun> gMaps : gun.values()) { Collections.sort(gMaps); } for (List<Radio> rMaps : radio.values()) { Collections.sort(rMaps); } for (List<Suspension> sMaps : suspension.values()) { Collections.sort(sMaps); } for (List<Turret> tMaps : turret.values()) { Collections.sort(tMaps); } } /** * Returns the list of modules for a given Tank and the specified * module type * @param t the compatible modules for this tank will be searched * @param type only the modules of this type will be returned * @return the list of specified modules for this tank */ public static List<? extends Module> getModules(Tank t, ModuleType type) { switch(type) { case Engine: return engine.get(t); case Radio: return radio.get(t); case Gun: return gun.get(t); case Suspension: return suspension.get(t); case Turret: return turret.get(t); default: return null; } } /** * Returns the list of modules for a given Tank and the specified * module type. * This is a desperate approach to teach java the purpose of generics. I * guess it is obvious that this is not an optimal solution... * @param <M> The module type * @param type the class that defines the module type * @param t the compatible modules for this tank will be searched * @return the list of specified modules for this tank */ public static <M> List getModules(Class<M> type, Tank t) { if (type.getSuperclass().isAssignableFrom(Module.class)) { // is module if(type.isAssignableFrom(Engine.class)) { return engine.get(t); } else if(type.isAssignableFrom(Gun.class)) { return gun.get(t); } else if(type.isAssignableFrom(Radio.class)) { return radio.get(t); } else if(type.isAssignableFrom(Suspension.class)) { return suspension.get(t); } else if(type.isAssignableFrom(Turret.class)) { return turret.get(t); } else { throw new IllegalArgumentException("Unknown Module Type " + type.getName()); } } else { // is not a module throw new IllegalArgumentException("This method accepts only subclasses " + "of 'Module' as parameters. Wrong parameter was " + type.getName()); } } /** * Retrieves the Module that is considered as "Top" or "Stock" for the specified * Tank. The type of module that is returned is defined by the given class. * @param <M> The module type * @param type the class that defines the module type * @param t the modules from this tank are retrieved * @param dev stock or top? * @return correct module (stock or top) */ public static <M> M getModuleByDev(Class<M> type, Tank t, Development dev) { List<M> modules = getModules(type, t); if (modules.isEmpty()) { try { /* * just return a new instance to prevent nullpointers. * for a detailed error report, use Evaluator.class * querying this here results in error flood for even the slightest anomality... */ return type.newInstance(); } catch (Exception e) { log.error("Error while instantiating a new module (for the " + "list of valid modules of this type was empty)", e); return null; } } else { switch (dev) { case Stock: return modules.get(0); case Top: return modules.get(modules.size() - 1); default: return null; } } } }