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 }