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    }