001 /////////////////////////////////////////////////////////////////////////////// 002 // Copyright (c) 2006, Frank S. Nestel, All Rights Reserved. 003 // 004 // This library is free software; you can redistribute it and/or 005 // modify it under the terms of the GNU Lesser General Public 006 // License as published by the Free Software Foundation; either 007 // version 2.1 of the License, or (at your option) any later version. 008 // 009 // This library is distributed in the hope that it will be useful, 010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 012 // GNU General Public License for more details. 013 // 014 // You should have received a copy of the GNU Lesser General Public 015 // License along with this program; if not, write to the Free Software 016 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 017 /////////////////////////////////////////////////////////////////////////////// 018 019 package de.spieleck.app.turn; 020 021 import java.io.File; 022 import java.io.FileOutputStream; 023 import java.io.PrintWriter; 024 import java.io.OutputStreamWriter; 025 import java.io.IOException; 026 import java.util.Arrays; 027 import java.util.LinkedHashMap; 028 import java.util.Iterator; 029 030 import org.apache.log4j.Logger; 031 032 /** 033 * Simple facade driver for the tournament system. 034 * The main() method commandline enables this facade. 035 * <br /> 036 * Note that this implementation implies a lot of (simple but useful) 037 * implications on how data is stored: 038 * <ul> 039 * <li>A tournament is stored in a directory</li> 040 * <li>The directory itself contains all setup information of the system 041 * <ul> 042 * <li>Player names in file <i>players</i></li> 043 * <li>The {@link PairingMode} in file <i>pairing</i></li> 044 * <li>The {@link SplittingMode}in file <i>splitting</i></li> 045 * <li>The {@link ScoringMode} in file <i>scoring</i></li> 046 * <li>The {@link RenderMode} in file <i>rendering</i></li> 047 * <li>The generated (and possible human modified) rounds 048 * in file <i>roundNN</i></li> 049 * </ul> 050 * </li> 051 * <li>The {@link de.spieleck.app.turn.rendering.RenderHtml} Renderer 052 * stored rendered information in subdirectory <i>out</i>.</li> 053 * </ul> 054 * It is thought that future Desktop or Webguis should keep this filesystem 055 * layout, thus allowing to switch between verious ways to interact with 056 * the internal datastorage. 057 * 058 * <p><a href="$URL: https://svn.sourceforge.net/svnroot/jtourney/src/de/spieleck/app/turn/Run.java $">$URL: https://svn.sourceforge.net/svnroot/jtourney/src/de/spieleck/app/turn/Run.java $</a></p> 059 * 060 * @author Frank S. Nestel 061 * @author $Author: nestefan $ 062 * @version $Revision: 2 $ $Date: 2006-03-20 14:33:27 +0100 (Mo, 20 Mrz 2006) $ $Author: nestefan $ 063 */ 064 public class Run 065 { 066 private final static Logger L = Logger.getLogger(Run.class); 067 068 public final static String BP = "de.spieleck.app.turn."; 069 070 public final static String PLAYERS = "players"; 071 072 public final static String PAIRING = "pairing"; 073 074 public final static String SCORING = "scoring"; 075 076 public final static String SPLITTING = "splitting"; 077 078 public final static String RENDERING = "rendering"; 079 080 public final static String ROUND = "round"; 081 082 /** Base directory for the tournament. */ 083 protected File root; 084 085 /** Scoring mode applied for the tournament. */ 086 protected ScoringMode scoringMode; 087 088 /** Method of dividing the players into tables/boards/matches. */ 089 protected SplittingMode splittingMode; 090 091 /** Method of rendering data for nice output. */ 092 protected RenderMode renderMode; 093 094 /** Central repository for all players. */ 095 protected PlayerRegistry pRegistry; 096 097 /** Current round. */ 098 protected int round; 099 100 /** Accumulated modification date of the whole data. */ 101 protected long totalLastModified; 102 103 /** Proxy class for ad hoc file serialization. */ 104 protected FileProxy fileProx; 105 106 /** Hold info, if setup information was complete */ 107 protected boolean setupComplete = true; 108 109 public Run() 110 { 111 } 112 113 public static void main(String[] args) 114 { 115 if ( args.length == 0 ) 116 { 117 L.error("First param is mandatory must be a (sub-)directory."); 118 return; 119 } 120 File fi = new File(args[0]); 121 if ( !fi.isDirectory() ) 122 { 123 L.error("First param must be a (sub-)directory."); 124 return; 125 } 126 Run run = new Run(); 127 try 128 { 129 run.init(fi); 130 int argNo = 1; 131 while ( argNo < args.length ) 132 { 133 L.info("try pairing "+ argNo+" of "+Arrays.asList(args)); 134 argNo = run.pairing(args, argNo); 135 } 136 run.finish(); 137 } 138 catch ( Exception ex ) 139 { 140 L.fatal("This is the end:",ex); 141 } 142 } 143 144 public void init(File root) 145 throws Exception 146 { 147 this.root = root; 148 // 149 scoringMode = (ScoringMode) initedInstance(SCORING); 150 splittingMode = (SplittingMode) initedInstance(SPLITTING); 151 renderMode = (RenderMode) initedInstance(RENDERING); 152 // 153 check4setup(PLAYERS); 154 if ( !setupComplete ) 155 { 156 L.warn("Setup was incomplete, please follow preceeding messages!"); 157 return; 158 } 159 // 160 pRegistry = new Player.StandardPlayerRegistry(scoringMode); 161 fileProx = new FileProxy(scoringMode, pRegistry); 162 // 163 renderMode.setRoot(root); 164 renderMode.startSession(); 165 renderMode.addSource(SCORING); 166 renderMode.addSource(SPLITTING); 167 renderMode.addSource(RENDERING); 168 // 169 // read player data 170 LineSource ls = new LineSource(root, PLAYERS); 171 fileProx.readPlayers(ls); 172 totalLastModified = ls.lastModified(); 173 renderMode.addSource(PLAYERS); 174 renderMode.render(totalLastModified, pRegistry.getPlayers(), 175 PLAYERS, "players", 0); 176 // 177 // create Standing 178 round = 0; 179 // Check should actually be true but this isn't nice either :-( 180 while ( grokRound(++round, false) ) 181 ; 182 } 183 184 protected boolean grokRound(int round, boolean check) 185 throws Exception 186 { 187 String roundName = FileProxy.makeRoundName(round); 188 LineSource ls; 189 try 190 { 191 ls = new LineSource(root, roundName); 192 } 193 catch ( IOException io ) 194 { 195 L.info("I cannot find round"+round); 196 return false; 197 } 198 renderMode.addSource(roundName); 199 long lastModified = ls.lastModified(); 200 totalLastModified = Math.max(totalLastModified,lastModified); 201 GameResult[] results = fileProx.readRound(ls, check); 202 renderMode.render(lastModified, results, ROUND+round+"-res", 203 "results", round); 204 Player[] ps = pRegistry.getPlayers(); 205 Arrays.sort(ps); 206 renderMode.render(totalLastModified,ps, ROUND+round+"-stand", 207 "standing", round); 208 // erzeuge eine Spielematrix 209 LinkedHashMap analyse = new LinkedHashMap(); 210 for(int i = 0; i < ps.length; i++) 211 { 212 int[] row = new int[ps.length]; 213 analyse.put(ps[i], row); 214 for(int j = 0; j < ps.length; j++) 215 { 216 row[j] = ps[i].getPlayedAgainst(ps[j]); 217 } 218 } 219 renderMode.render(totalLastModified,analyse, roundName+"-analyse", 220 "analyse", round); 221 return true; 222 } 223 224 public int pairing(String[] args, int start) 225 throws Exception 226 { 227 PairingMode pair = (PairingMode) instance(args[start],BP+PAIRING); 228 int nextStart = pair.init(args, start+1); 229 Game[] games = pair.pairing(round, pRegistry, splittingMode); 230 if ( games == null ) 231 { 232 L.error("No pairing returned!"); 233 return nextStart; 234 } 235 String roundName = FileProxy.makeRoundName(round); 236 renderMode.render(games, roundName, "pairing", round); 237 PrintWriter bw = writer(roundName); 238 renderMode.addSource(roundName); 239 // XXX Ugly rebuild of Game to GameResult 240 GameResult[] gameResults = new GameResult[games.length]; 241 for(int i = 0; i < gameResults.length; i++) 242 { 243 gameResults[i] = new GameResult(games[i].getId()); 244 Iterator<Player> iter = games[i].players(); 245 while ( iter.hasNext() ) 246 { 247 gameResults[i].add(iter.next(), scoringMode.zeroScore()); 248 } 249 } 250 // 251 fileProx.writeRound(bw, 252 "round="+round 253 +" active="+pRegistry.activeCount() 254 +" "+PAIRING+"="+pair 255 +" "+SCORING+"="+scoringMode 256 +" "+SPLITTING+"="+splittingMode, 257 gameResults 258 ); 259 grokRound(round++, false); 260 return nextStart; 261 } 262 263 public void finish() 264 throws IOException 265 { 266 // Can happen in case of incomplete Setup 267 if ( renderMode != null ) 268 renderMode.endSession(); 269 } 270 271 public static Object instance(String name, String defaultPackage) 272 { 273 Class clazz = null; 274 try 275 { 276 clazz = Class.forName(name); 277 } 278 catch ( ClassNotFoundException ex ) 279 { 280 try 281 { 282 clazz = Class.forName(defaultPackage+"."+name); 283 } 284 catch ( ClassNotFoundException ex2 ) 285 { 286 // Hmm, we could set an default 287 } 288 } 289 if ( clazz == null ) 290 { 291 L.error("Cannot create class <"+name+"> @ "+defaultPackage); 292 } 293 try 294 { 295 return clazz.newInstance(); 296 } 297 catch ( Exception ie ) 298 { 299 L.error("Cannot create instance of <"+clazz+">"); 300 } 301 return null; 302 } 303 304 public LineSourceInited initedInstance(String rName) 305 throws IOException 306 { 307 if ( check4setup(rName) ) 308 return null; 309 LineSource ls = new LineSource(root, rName); 310 LineSourceInited o = (LineSourceInited) instance(ls.line(), BP+rName); 311 o.init(ls); 312 return o; 313 } 314 315 public boolean check4setup(String rName) 316 throws IOException 317 { 318 if ( Util.check4file(getClass(), root, rName, null, "tourney.tpl/") ) 319 { 320 L.warn(rName+" created from template, please edit appropriately."); 321 setupComplete = false; 322 return true; 323 } 324 else 325 { 326 L.debug(rName+" was found."); 327 } 328 return false; 329 } 330 331 public PrintWriter writer(String name) 332 throws IOException 333 { 334 FileOutputStream os = new FileOutputStream(new File(root, name)); 335 OutputStreamWriter osw = new OutputStreamWriter(os, "UTF-8"); 336 return new PrintWriter(osw); 337 } 338 339 }