webidor/src/main/java/cz/xelfi/quoridor/webidor/resources/Games.java
author Jaroslav Tulach <jtulach@netbeans.org>
Thu, 01 Oct 2009 06:06:55 +0200
changeset 117 c1057591a344
parent 115 6a80463a74c0
child 128 eba04a2569d0
permissions -rw-r--r--
Specifying UTF-8 when reading and writing files
     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * The contents of this file are subject to the terms of either the GNU
     5  * General Public License Version 2 only ("GPL") or the Common
     6  * Development and Distribution License("CDDL") (collectively, the
     7  * "License"). You may not use this file except in compliance with the
     8  * License. You can obtain a copy of the License at
     9  * http://www.netbeans.org/cddl-gplv2.html
    10  * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
    11  * specific language governing permissions and limitations under the
    12  * License.  When distributing the software, include this License Header
    13  * Notice in each file and include the License file at
    14  * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
    15  * particular file as subject to the "Classpath" exception as provided
    16  * by Sun in the GPL Version 2 section of the License file that
    17  * accompanied this code. If applicable, add the following below the
    18  * License Header, with the fields enclosed by brackets [] replaced by
    19  * your own identifying information:
    20  * "Portions Copyrighted [year] [name of copyright owner]"
    21  *
    22  * Contributor(s):
    23  *
    24  * Portions Copyrighted 2009 Jaroslav Tulach
    25  */
    26 
    27 package cz.xelfi.quoridor.webidor.resources;
    28 
    29 import cz.xelfi.quoridor.IllegalPositionException;
    30 import cz.xelfi.quoridor.webidor.*;
    31 import cz.xelfi.quoridor.Move;
    32 import java.io.BufferedReader;
    33 import java.io.BufferedWriter;
    34 import java.io.File;
    35 import java.io.FileInputStream;
    36 import java.io.FileOutputStream;
    37 import java.io.FileReader;
    38 import java.io.FileWriter;
    39 import java.io.IOException;
    40 import java.io.InputStream;
    41 import java.io.InputStreamReader;
    42 import java.io.OutputStreamWriter;
    43 import java.io.PrintWriter;
    44 import java.io.Writer;
    45 import java.util.ArrayList;
    46 import java.util.Collections;
    47 import java.util.Date;
    48 import java.util.List;
    49 import java.util.logging.Level;
    50 import java.util.logging.Logger;
    51 import java.util.regex.Matcher;
    52 import java.util.regex.Pattern;
    53 import javax.ws.rs.DefaultValue;
    54 import javax.ws.rs.GET;
    55 import javax.ws.rs.POST;
    56 import javax.ws.rs.PUT;
    57 import javax.ws.rs.Path;
    58 import javax.ws.rs.PathParam;
    59 import javax.ws.rs.Produces;
    60 import javax.ws.rs.QueryParam;
    61 import javax.ws.rs.WebApplicationException;
    62 import javax.ws.rs.core.MediaType;
    63 import javax.ws.rs.core.Response.Status;
    64 
    65 /**
    66  *
    67  * @author Jaroslav Tulach <jtulach@netbeans.org>
    68  */
    69 public final class Games {
    70     private final Quoridor quoridor;
    71     private final List<Game> games = new ArrayList<Game>();
    72     private final File dir;
    73     private static final Logger LOG = Logger.getLogger(Games.class.getName());
    74 
    75     public Games(File dir, Quoridor quoridor) {
    76         this.dir = dir;
    77         this.quoridor = quoridor;
    78         File[] arr = dir.listFiles();
    79         if (arr != null) {
    80             for (File f : arr) {
    81                 try {
    82                     Game g = readGame(f);
    83                     games.add(g);
    84                 } catch (IOException ex) {
    85                     LOG.log(Level.WARNING, "Wrong game in " + f, ex);
    86                 }
    87             }
    88         }
    89     }
    90 
    91     @POST
    92     @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
    93     public GameId createGame(
    94         @QueryParam("loginID") String id,
    95         @QueryParam("white") String white,
    96         @QueryParam("black") String black
    97     ) throws IOException {
    98         String logUser = quoridor.isLoggedIn(id);
    99         if (logUser == null) {
   100             throw new WebApplicationException(Status.UNAUTHORIZED);
   101         }
   102         if (white == null) {
   103             throw new WebApplicationException(Status.PRECONDITION_FAILED);
   104         }
   105         if (black == null) {
   106             throw new WebApplicationException(Status.PRECONDITION_FAILED);
   107         }
   108         if (!logUser.equals(white) && !logUser.equals(black)) {
   109             throw new WebApplicationException(Status.PRECONDITION_FAILED);
   110         }
   111 
   112         Game g = new Game(white, black);
   113         storeGame(g);
   114         games.add(g);
   115         return g.getId();
   116     }
   117 
   118     @GET
   119     @Path("{id}")
   120     @Produces(MediaType.TEXT_PLAIN)
   121     public String getBoard(
   122         @PathParam("id") String id,
   123         @QueryParam("move") @DefaultValue("-1") int move
   124     ) {
   125         Game g = findGame(id, move);
   126         if (g == null) {
   127             throw new IllegalArgumentException("Unknown game " + id);
   128         }
   129         return g.getBoard().toString();
   130     }
   131 
   132     @GET
   133     @Path("{id}")
   134     @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
   135     public Game getBoardInfo(
   136         @PathParam("id") String id,
   137         @QueryParam("move") @DefaultValue("-1") int move
   138     ) {
   139         return findGame(id, move);
   140     }
   141 
   142     @PUT
   143     @Path("{id}")
   144     @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
   145     public GameId applyMove(
   146         @QueryParam("loginID") String loginId,
   147         @PathParam("id") String id,
   148         @QueryParam("player") String player,
   149         @QueryParam("move") String move,
   150         @QueryParam("comment") String comment
   151     ) throws IllegalPositionException {
   152         String logUser = quoridor.isLoggedIn(loginId);
   153         if (logUser == null) {
   154             throw new WebApplicationException(Status.UNAUTHORIZED);
   155         }
   156         if (!logUser.equals(player)) {
   157             throw new WebApplicationException(Status.UNAUTHORIZED);
   158         }
   159         if (comment == null && move == null) {
   160             throw new WebApplicationException(Status.BAD_REQUEST);
   161         }
   162 
   163         Game g = findGame(id);
   164         if (g == null) {
   165             throw new IllegalArgumentException("Unknown game " + id);
   166         }
   167         if (move != null) {
   168             Move m = Move.valueOf(move);
   169             g.apply(player, m, new Date());
   170         }
   171         if (comment != null) {
   172             g.comment(player, comment, new Date());
   173         }
   174         try {
   175             storeGame(g);
   176         } catch (IOException ex) {
   177             LOG.log(Level.WARNING, "Cannot store game " + id, ex);
   178         }
   179         return g.getId();
   180     }
   181 
   182     @GET
   183     @Produces({MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
   184     public List<GameId> listGames() {
   185         List<GameId> arr = new ArrayList<GameId>(games.size());
   186         for (Game g : games) {
   187             arr.add(g.getId());
   188         }
   189         Collections.sort(arr, GameId.NEWEST_FIRST);
   190         return arr;
   191     }
   192 
   193     public List<Game> getGames() {
   194         return games;
   195     }
   196 
   197     private Game findGame(String id) {
   198         for (Game g : games) {
   199             if (g.getId().getId().equals(id)) {
   200                 return g;
   201             }
   202         }
   203         return null;
   204     }
   205     private Game findGame(String id, int move) {
   206         Game g = findGame(id);
   207         if (g == null) {
   208             throw new IllegalArgumentException("Unknown game " + id);
   209         }
   210         try {
   211             return move == -1 ? g : g.snapshot(move);
   212         } catch (IllegalPositionException ex) {
   213             Logger.getLogger(Games.class.getName()).log(Level.SEVERE, null, ex);
   214             return null;
   215         }
   216     }
   217 
   218     private static final Pattern saidWho = Pattern.compile("# *([^ ]*) *@(.*):$");
   219 
   220     private Game readGame(File f) throws IOException {
   221         InputStream is = new FileInputStream(f);
   222         BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
   223         String white = null;
   224         String black = null;
   225         Game g = null;
   226         String who = null;
   227         String when = null;
   228         for (;;) {
   229             String line = r.readLine();
   230             if (line == null) {
   231                 line = "finish";
   232             }
   233             line = line.trim();
   234             if (line.length() == 0) {
   235                 continue;
   236             }
   237             if (line.startsWith("# white: ")) {
   238                 white = line.substring(9);
   239                 continue;
   240             }
   241             if (line.startsWith("# black: ")) {
   242                 black = line.substring(9);
   243                 continue;
   244             }
   245             if (line.startsWith("#")) {
   246                 Matcher m =saidWho.matcher(line);
   247                 if (m.matches()) {
   248                     who = m.group(1);
   249                     when = m.group(2);
   250                     continue;
   251                 }
   252                 if (g == null) {
   253                     continue;
   254                 }
   255                 if (line.startsWith("# ")) {
   256                     line = line.substring(2);
   257                 } else {
   258                     line = line.substring(1);
   259                 }
   260                 Date d = new Date();
   261                 try {
   262                     if (when != null) {
   263                         d = new Date(Date.parse(when));
   264                     }
   265                 } catch (IllegalArgumentException ex) {
   266                     LOG.warning("Unparseable date " + when + " in " + f);
   267                 }
   268                 g.comment(who, line, d);
   269                 who = null;
   270                 when = null;
   271                 continue;
   272             }
   273             if (white == null || black == null) {
   274                 throw new IOException("Missing white and black identification in " + f);
   275             }
   276             if (g == null) {
   277                 GameId id = new GameId(f.getName(), white, black, new Date(f.lastModified()), new Date(f.lastModified()), GameStatus.whiteMove);
   278                 g = new Game(id);
   279             }
   280             int hash = line.indexOf('#');
   281             if (hash >= 0) {
   282                 line = line.substring(0, hash);
   283             }
   284             if (line.equals("finish")) {
   285                 break;
   286             }
   287             String[] moves = line.split(" ");
   288             if (moves.length == 0) {
   289                 continue;
   290             }
   291             if (moves.length > 2) {
   292                 throw new IOException("Too much moves on line: " + line);
   293             }
   294             try {
   295                 if (!"...".equals(moves[0])) {
   296                     g.apply(white, Move.valueOf(moves[0]), null);
   297                 }
   298                 if (moves.length == 2) {
   299                     g.apply(black, Move.valueOf(moves[1]), null);
   300                 }
   301             } catch (IllegalPositionException ex) {
   302                 throw new IOException("Wrong move: " + ex.getMessage());
   303             }
   304         }
   305         if (g == null) {
   306             throw new IOException("No moves in " + f);
   307         }
   308         return g;
   309     }
   310 
   311     private void storeGame(Game g) throws IOException {
   312         dir.mkdirs();
   313         File f = new File(dir, g.getId().getId());
   314         storeGame(g, f);
   315     }
   316 
   317     final void storeGame(Game g, File f) throws IOException {
   318         FileOutputStream os = new FileOutputStream(f);
   319         Writer w = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
   320         PrintWriter pw = new PrintWriter(w);
   321         pw.println("# white: " + g.getId().getWhite());
   322         pw.println("# black: " + g.getId().getBlack());
   323         pw.println("# status: " + g.getId().getStatus());
   324         int cnt = 0;
   325         boolean separate = true;
   326         for (CommentedMove m : g.getMoves()) {
   327             if (!separate && cnt % 2 == 1) {
   328                 pw.print("... ");
   329             }
   330             separate = true;
   331             pw.print(m.getMove().toString());
   332             List<Note> notes = m.getComments();
   333             if (notes != null) {
   334                 separate = false;
   335                 pw.println();
   336                 for (Note n : notes) {
   337                     pw.print ("# ");
   338                     pw.print(n.getWho());
   339                     pw.print("@");
   340                     pw.print(n.getWhen());
   341                     pw.println(":");
   342                     for (String l : n.getComment().split("\n")) {
   343                         pw.print("# ");
   344                         pw.println(l);
   345                     }
   346                 }
   347             }
   348 
   349             cnt++;
   350 
   351             if (separate) {
   352                 if (cnt % 2 == 0) {
   353                     pw.println();
   354                 } else {
   355                     pw.print(' ');
   356                 }
   357             }
   358         }
   359         pw.println();
   360         pw.println();
   361         pw.flush();
   362         pw.close();
   363         w.close();
   364     }
   365 }