2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
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]"
24 * Portions Copyrighted 2009 Jaroslav Tulach
27 package cz.xelfi.quoridor.webidor.resources;
29 import com.sun.jersey.api.json.JSONWithPadding;
30 import cz.xelfi.quoridor.IllegalPositionException;
31 import cz.xelfi.quoridor.webidor.*;
32 import cz.xelfi.quoridor.Move;
33 import java.io.BufferedReader;
34 import java.io.BufferedWriter;
36 import java.io.FileInputStream;
37 import java.io.FileOutputStream;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.io.InputStreamReader;
41 import java.io.OutputStreamWriter;
42 import java.io.PrintWriter;
43 import java.io.Writer;
44 import java.util.ArrayList;
45 import java.util.Collections;
46 import java.util.Date;
47 import java.util.List;
48 import java.util.logging.Level;
49 import java.util.logging.Logger;
50 import java.util.regex.Matcher;
51 import java.util.regex.Pattern;
52 import javax.ws.rs.DefaultValue;
53 import javax.ws.rs.GET;
54 import javax.ws.rs.POST;
55 import javax.ws.rs.PUT;
56 import javax.ws.rs.Path;
57 import javax.ws.rs.PathParam;
58 import javax.ws.rs.Produces;
59 import javax.ws.rs.QueryParam;
60 import javax.ws.rs.WebApplicationException;
61 import javax.ws.rs.core.GenericEntity;
62 import javax.ws.rs.core.MediaType;
63 import javax.ws.rs.core.Response.Status;
67 * @author Jaroslav Tulach <jtulach@netbeans.org>
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());
75 public Games(File dir, Quoridor quoridor) {
77 this.quoridor = quoridor;
78 File[] arr = dir.listFiles();
84 } catch (IOException ex) {
85 LOG.log(Level.WARNING, "Wrong game in " + f, ex);
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);
103 throw new WebApplicationException(Status.PRECONDITION_FAILED);
106 throw new WebApplicationException(Status.PRECONDITION_FAILED);
108 if (!logUser.equals(white) && !logUser.equals(black)) {
109 throw new WebApplicationException(Status.PRECONDITION_FAILED);
112 Game g = new Game(white, black);
120 @Produces(MediaType.TEXT_PLAIN)
121 public String getBoard(
122 @PathParam("id") String id,
123 @QueryParam("move") @DefaultValue("-1") int move
125 Game g = findGame(id, move);
127 throw new IllegalArgumentException("Unknown game " + id);
129 return g.getBoard().toString();
132 public Game getBoardInfo(
133 @QueryParam("loginID") @DefaultValue("") String loginId,
134 @PathParam("id") String id,
135 @QueryParam("move") @DefaultValue("-1") int move
136 ) throws IOException {
137 Game g = findGame(id, move);
138 if (canSee(g.getId(), loginId)) {
141 throw new WebApplicationException(Status.UNAUTHORIZED);
146 @Produces({ "application/x-javascript", MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
147 public JSONWithPadding getBoardInfo(
148 @QueryParam("callback") String callback,
149 @QueryParam("loginID") @DefaultValue("") String loginId,
150 @PathParam("id") String id,
151 @QueryParam("move") @DefaultValue("-1") int move
152 ) throws IOException {
153 return new JSONWithPadding(getBoardInfo(loginId, id, move), callback);
157 private boolean canSee(GameId id, String loginId) throws IOException {
158 if (!id.isFinished()) {
161 String logUser = quoridor.isLoggedIn(loginId);
162 if (logUser == null) {
165 if (logUser.equals(id.getWhite())) {
168 if (logUser.equals(id.getBlack())) {
171 User info = quoridor.getUsers().getUserInfo(loginId, logUser);
172 if (info != null && info.hasPermission("games")) {
180 @Produces({ MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
181 public GameId applyMove(
182 @QueryParam("loginID") String loginId,
183 @PathParam("id") String id,
184 @QueryParam("player") String player,
185 @QueryParam("move") String move,
186 @QueryParam("comment") String comment
187 ) throws IllegalPositionException, IOException {
188 String logUser = quoridor.isLoggedIn(loginId);
189 if (logUser == null) {
190 throw new WebApplicationException(Status.UNAUTHORIZED);
192 if (!logUser.equals(player)) {
193 User info = quoridor.getUsers().getUserInfo(loginId, logUser);
194 if (info == null || !info.hasPermission("resign")) {
195 throw new WebApplicationException(Status.UNAUTHORIZED);
198 if (comment == null && move == null) {
199 throw new WebApplicationException(Status.BAD_REQUEST);
202 Game g = findGame(id);
204 throw new IllegalArgumentException("Unknown game " + id);
207 Move m = Move.valueOf(move);
208 g.apply(player, m, new Date());
210 if (comment != null) {
211 g.comment(player, comment, new Date());
215 } catch (IOException ex) {
216 LOG.log(Level.WARNING, "Cannot store game " + id, ex);
222 @Produces({ "application/x-javascript", MediaType.APPLICATION_JSON, MediaType.TEXT_XML })
223 public JSONWithPadding listGames(
224 @QueryParam("callback") String callback,
225 @DefaultValue("") @QueryParam("loginID") String loginId,
226 @DefaultValue("") @QueryParam("status") String status
227 ) throws IOException {
228 List<GameId> arr = new ArrayList<GameId>(games.size());
229 for (Game g : games) {
230 if (!canSee(g.getId(), loginId)) {
233 if (status.length() == 0 || g.getId().getStatus().toString().equals(status)) {
237 Collections.sort(arr, GameId.NEWEST_FIRST);
238 return new JSONWithPadding(new GenericEntity<List<GameId>>(arr) {}, callback);
241 public List<Game> getGames() {
245 private Game findGame(String id) {
246 for (Game g : games) {
247 if (g.getId().getId().equals(id)) {
253 private Game findGame(String id, int move) {
254 Game g = findGame(id);
256 throw new IllegalArgumentException("Unknown game " + id);
259 return move == -1 ? g : g.snapshot(move);
260 } catch (IllegalPositionException ex) {
261 Logger.getLogger(Games.class.getName()).log(Level.SEVERE, null, ex);
266 private static final Pattern saidWho = Pattern.compile("# *([^ ]*) *@(.*):$");
268 private Game readGame(File f) throws IOException {
269 InputStream is = new FileInputStream(f);
270 BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
277 String line = r.readLine();
282 if (line.length() == 0) {
285 if (line.startsWith("# white: ")) {
286 white = line.substring(9);
289 if (line.startsWith("# black: ")) {
290 black = line.substring(9);
293 if (line.startsWith("#")) {
294 Matcher m =saidWho.matcher(line);
303 if (line.startsWith("# ")) {
304 line = line.substring(2);
306 line = line.substring(1);
311 d = new Date(Date.parse(when));
313 } catch (IllegalArgumentException ex) {
314 LOG.warning("Unparseable date " + when + " in " + f);
316 g.comment(who, line, d);
321 if (white == null || black == null) {
322 throw new IOException("Missing white and black identification in " + f);
325 GameId id = new GameId(f.getName(), white, black, new Date(f.lastModified()), new Date(f.lastModified()), GameStatus.whiteMove, 0, false);
328 int hash = line.indexOf('#');
330 line = line.substring(0, hash);
332 if (line.equals("finish")) {
335 String[] moves = line.split(" ");
336 if (moves.length == 0) {
339 if (moves.length > 2) {
340 throw new IOException("Too much moves on line: " + line);
343 if (!"...".equals(moves[0])) {
344 g.apply(white, Move.valueOf(moves[0]), null);
346 if (moves.length == 2) {
347 g.apply(black, Move.valueOf(moves[1]), null);
349 } catch (IllegalPositionException ex) {
350 throw new IOException("Wrong move: " + ex.getMessage());
354 throw new IOException("No moves in " + f);
359 private void storeGame(Game g) throws IOException {
361 File f = new File(dir, g.getId().getId());
365 final void storeGame(Game g, File f) throws IOException {
366 FileOutputStream os = new FileOutputStream(f);
367 Writer w = new BufferedWriter(new OutputStreamWriter(os, "UTF-8"));
368 PrintWriter pw = new PrintWriter(w);
369 pw.println("# white: " + g.getId().getWhite());
370 pw.println("# black: " + g.getId().getBlack());
371 pw.println("# status: " + g.getId().getStatus());
373 boolean separate = true;
374 for (CommentedMove m : g.getMoves()) {
375 if (!separate && cnt % 2 == 1) {
379 pw.print(m.getMove().toString());
380 List<Note> notes = m.getComments();
384 for (Note n : notes) {
386 pw.print(n.getWho());
388 pw.print(n.getWhen());
390 for (String l : n.getComment().split("\n")) {