/* * 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.xml; import java.io.FileNotFoundException; import java.io.PrintWriter; import java.util.List; 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.Module; import de.nx42.wotcrawler.db.tank.Tank; import de.nx42.wotcrawler.db.tank.Tank.TankType; import de.nx42.wotcrawler.ext.Field; import de.nx42.wotcrawler.ext.FieldDef; import de.nx42.wotcrawler.ext.ModuleMap; import de.nx42.wotcrawler.ext.TankRating; /** * This class is used to transform the database contents into other formats. * * @author Sebastian Straub <sebastian-straub@gmx.net> */ public class Transformer { private static final Logger log = LoggerFactory.getLogger(Transformer.class); /** The database to transform */ protected TanksDB db; /** The ModuleMap, mapping from each tank to a list of compatible modules */ protected ModuleMap mm; /** * Initializes the transformer with the specified TanksDB * @param db the database to work with */ public Transformer(TanksDB db) { this.db = db; this.mm = ModuleMap.build(db); } // -------------------- generic html table creation -------------------- /** * Writes a html table containing all tanks with stock and top equipment * and the specified fields. All fields are allowed except for the rating * fields (prefix RT_ and RT2_) * @param dest the file where the table shall be stored in * @param fields the fields that shall be stored in the table */ public void writeTableTank(String dest, Field[] fields) { try { PrintWriter out = new PrintWriter(dest); out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<html><body>\n\n"); out.write(buildTankTable(fields)); out.write("\n\n</body></html>"); out.flush(); out.close(); } catch (FileNotFoundException ex) { log.error("Could not write to local file: Not found!", ex); } } // -------------------- specific html tables -------------------- /** * Writes a table at the specified location, containing many details about * each tank * @param dest the file where the table shall be stored in */ public void writeTableTankDetailed(String dest) { writeTableTank(dest, FieldDef.detailed_Combined); } /** * Writes a table containing all tanks, their parents and children and * compatible modules, as well as one table for each module type. * There are html anchors between all tanks and modules, but prepare for * big tables... * @param dest the file where the tables shall be stored in */ public void writeTablesLinked(String dest) { try { PrintWriter out = new PrintWriter(dest); out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<html><body><table>\n\n"); out.write(buildTankTable(FieldDef.tank_base)); out.write("</table>\n\n"); out.write(buildModuleTables()); out.write("\n\n\n</body></html>"); out.flush(); out.close(); } catch (FileNotFoundException ex) { log.error("Could not write to local file: Not found!", ex); } } /** * Writes a table for each tank type, containing the ratings for each tank, * as defined by the TankRating class. * @param dest the file where the tables shall be stored in */ public void writeRatingTable(String dest) { try { PrintWriter out = new PrintWriter(dest); out.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<html><body>\n\n"); out.write("\n\n\n<!-- light -->\n\n"); out.write(buildSingleRatingTable(TankType.LightTank, FieldDef.rating)); out.write("\n\n\n<!-- medium -->\n\n"); out.write(buildSingleRatingTable(TankType.MediumTank, FieldDef.rating)); out.write("\n\n\n<!-- heavy -->\n\n"); out.write(buildSingleRatingTable(TankType.HeavyTank, FieldDef.rating)); out.write("\n\n\n<!-- td -->\n\n"); out.write(buildSingleRatingTable(TankType.TankDestroyer, FieldDef.rating)); out.write("\n\n\n<!-- spg -->\n\n"); out.write(buildSingleRatingTable(TankType.SelfPropelledGun, FieldDef.rating)); out.write("\n\n\n</body></html>"); out.flush(); out.close(); } catch (FileNotFoundException ex) { log.error("Could not write to local file: Not found!", ex); } } // -------------------- tables only -------------------- /** * Builds a single html table, containing all the specified fields. * Note: All fields are allowed except for the rating fields (prefix RT_ and RT2_) * @param fields the fields that shall be stored in the table * @return the HTML table as string */ protected String buildTankTable(Field[] fields) { StringBuilder sb = new StringBuilder(fields.length * 2 * 20); sb.append("<table>\n"); // head sb.append("<thead>\n<tr>\n"); for (Field f : fields) { sb.append("\t<th>"); sb.append(f.toString()); sb.append("</th>\n"); } sb.append("</tr>\n</thead>\n"); // body sb.append("<tbody>\n"); for (Tank t : db.tanks) { for (Development dev : Development.values()) { sb.append(String.format("<tr id=\"%s\">\n", t.id)); for (Field field : fields) { sb.append("\t<td>"); sb.append(convertField(field, t, dev)); sb.append("</td>\n"); } sb.append("</tr>\n"); } } sb.append("</tbody>\n"); sb.append("</table>\n"); return sb.toString(); } /** * Builds 5 tables, one for each module type, with the specified fields * for each module type * @param engine the fields that shall be stored in the engine table * @param gun the fields that shall be stored in the gun table * @param radio the fields that shall be stored in the radio table * @param suspension the fields that shall be stored in the suspension table * @param turret the fields that shall be stored in the turret table * @return all the html tables in one long string... */ protected String buildModuleTables(Field[] engine, Field[] gun, Field[] radio, Field[] suspension, Field[] turret) { StringBuilder sb = new StringBuilder(100000); sb.append("\n\n\n<!-- engines -->\n\n"); sb.append(buildSingleModuleTable(db.modules.engines, engine)); sb.append("\n\n\n<!-- guns -->\n\n"); sb.append(buildSingleModuleTable(db.modules.guns, gun)); sb.append("\n\n\n<!-- radios -->\n\n"); sb.append(buildSingleModuleTable(db.modules.radios, radio)); sb.append("\n\n\n<!-- suspensions -->\n\n"); sb.append(buildSingleModuleTable(db.modules.suspensions, suspension)); sb.append("\n\n\n<!-- turrets -->\n\n"); sb.append(buildSingleModuleTable(db.modules.turrets, turret)); return sb.toString(); } /** * Builds 5 tables, one for each module type, using all available fields * for each module type * @return all the html tables in one long string... */ protected String buildModuleTables() { return buildModuleTables(FieldDef.mod_engine, FieldDef.mod_gun, FieldDef.mod_radio, FieldDef.mod_suspension, FieldDef.mod_turret); } /** * Creates a table from a list of modules and an array of fields the table * shall contain. Also creates a header with ugly names... * Restrictions: Use only one type of module, use only fields that are * covered by this module! * For different module types, you got to create a new table for each module * type. * * @param modules this should be <code>List<Module></code>, but as java * eliminates types at compilation, this is not possible, but don't expect * that this will work for anything but Modules or their subclasses! * @param fields the fields that shall be contained in the table. choose * only fields that are compatible with this module (watch out for prefixes...) * @return HTML table containing the modules in the list and the fields from * the array. */ protected String buildSingleModuleTable(List<? extends Module> modules, Field[] fields) { StringBuilder sb = new StringBuilder(20000); sb.append("<table>\n"); // head sb.append("<thead>\n<tr>\n"); for (Field f : fields) { sb.append("\t<th>"); sb.append(f.toString()); sb.append("</th>\n"); } sb.append("</tr>\n</thead>\n"); // body sb.append("<tbody>\n"); for (Object o : modules) { Module m = (Module) o; sb.append(String.format("<tr id=\"%s\">\n", m.name)); for (Field f : fields) { sb.append("\t<td>"); sb.append(f.get(m)); sb.append("</td>\n"); } sb.append("</tr>\n"); } sb.append("</tbody>\n"); sb.append("</table>\n"); return sb.toString(); } /** * Generates a single table containing all tanks of the specified type. * The rating Fields are supported here, as well as all other fields. * Note that for the rating fields, tooltips will be created that show the * actual values behind the ratings. * * @param type the tanks that shall be stored in this table (ratings are * always bound to one type - apples and oranges) * @param fields the fields that shall be contained in the table * @return the rating table as string */ protected String buildSingleRatingTable(TankType type, Field[] fields) { /* * calculate minmax for this tanktype * calculate rating for each tank and write in sb * * -> if minmax is calculated staticly but for this type only, * multithreaded access to enum Field is not allowed! */ Field.calculateMinMaxFields(db, type); StringBuilder sb = new StringBuilder(); sb.append("<table>\n"); // head sb.append("<thead>\n<tr>\n"); for (Field f : fields) { sb.append("\t<th>"); sb.append(f.toString()); sb.append("</th>\n"); } sb.append("</tr>\n</thead>\n"); // body sb.append("<tbody>\n"); for (Tank t : db.tanks) { if (t.type == type) { for (Development dev : Development.values()) { // calculate rating TankRating tr = new TankRating(t, dev); tr.calculateRatings(); // write table entry sb.append(String.format("<tr id=\"%s\">\n", t.id)); for (Field field : fields) { addCell(tr, sb, field); } sb.append("</tr>\n"); } } } sb.append("</tbody>\n"); sb.append("</table>\n"); return sb.toString(); } // -------------------- helpers -------------------- /** * Adds a cell to the stringbuilder. If the cell contains the special field * EMPTY, an empty td with class "empty" is added, else the contents of this * cell's get() method are stored within td tags * @param tr the tankrating (as source for the cell valie) * @param sb the stringbuilder, where the result is added * @param f the field to retrieve the value from */ protected static void addCell(TankRating tr, StringBuilder sb, Field f) { if (f == Field.EMPTY) { sb.append("\t<td class=\"empty\"></td>\n"); } else { sb.append("\t<td>"); sb.append(f.get(tr)); sb.append("</td>\n"); } } /** * Retrieves the value of a field and converts its contents with integer or * decimal value 0 to n/a (except for fields where 0 is a valid value, * such as Cost or GunArc) * @param field the field to retrieve the value from * @param t the Tank to calculate the value for * @param dev the development (needed if field depends on it) * @return the converted field value */ protected String convertField(Field field, Tank t, Development dev) { String f = field.get(t, dev); if(f.startsWith("0") && field != Field.T_GunArc_Left && field != Field.T_Cost) { if("0".equals(f)) { return Field.na; } else if(f.startsWith("0.0")) { return Field.na; } } return f; } }