Support for comments among the moves
authorJaroslav Tulach <jaroslav.tulach@apidesign.org>
Mon, 28 Sep 2009 14:42:19 +0200
changeset 1156a80463a74c0
parent 114 ed560bfe37f0
child 116 5263ee1916e5
Support for comments among the moves
freemarkerdor/pom.xml
freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle.properties
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle_cs.properties
freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/game.fmt
webidor/pom.xml
webidor/src/main/java/cz/xelfi/quoridor/webidor/CommentedMove.java
webidor/src/main/java/cz/xelfi/quoridor/webidor/Game.java
webidor/src/main/java/cz/xelfi/quoridor/webidor/Note.java
webidor/src/main/java/cz/xelfi/quoridor/webidor/resources/Games.java
webidor/src/test/java/cz/xelfi/quoridor/webidor/QuoridorTest.java
webidor/src/test/java/cz/xelfi/quoridor/webidor/resources/ChatTest.java
     1.1 --- a/freemarkerdor/pom.xml	Sat Sep 26 21:47:02 2009 +0200
     1.2 +++ b/freemarkerdor/pom.xml	Mon Sep 28 14:42:19 2009 +0200
     1.3 @@ -10,13 +10,13 @@
     1.4    <groupId>org.apidesign</groupId>
     1.5    <artifactId>freemarkerdor</artifactId>
     1.6    <name>freemarkerdor</name>
     1.7 -  <version>1.26</version>
     1.8 +  <version>1.30</version>
     1.9    <url>http://maven.apache.org</url>
    1.10    <dependencies>
    1.11      <dependency>
    1.12        <groupId>${project.groupId}</groupId>
    1.13        <artifactId>webidor</artifactId>
    1.14 -      <version>1.3</version>
    1.15 +      <version>1.4</version>
    1.16      </dependency>
    1.17      <dependency>
    1.18        <groupId>org.netbeans.modules</groupId>
     2.1 --- a/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java	Sat Sep 26 21:47:02 2009 +0200
     2.2 +++ b/freemarkerdor/src/main/java/cz/xelfi/quoridor/freemarkerdor/UI.java	Mon Sep 28 14:42:19 2009 +0200
     2.3 @@ -252,6 +252,26 @@
     2.4      }
     2.5  
     2.6      @GET
     2.7 +    @Path("games/{id}/comment")
     2.8 +    @Produces(MediaType.TEXT_HTML)
     2.9 +    public Response comment(
    2.10 +        @PathParam("id") String id,
    2.11 +        @QueryParam("comment") String comment
    2.12 +    ) {
    2.13 +        Viewable v = checkLogin();
    2.14 +        if (v != null) {
    2.15 +            return Response.ok().entity(v).build();
    2.16 +        }
    2.17 +        WebResource wr = base.path("games").path(id).
    2.18 +            queryParam("loginID", uuid).
    2.19 +            queryParam("player", user).
    2.20 +            queryParam("comment", comment);
    2.21 +        wr.put();
    2.22 +        
    2.23 +        return board(id);
    2.24 +    }
    2.25 +
    2.26 +    @GET
    2.27      @Path("games/create")
    2.28      @Produces(MediaType.TEXT_HTML)
    2.29      public Response create(
     3.1 --- a/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle.properties	Sat Sep 26 21:47:02 2009 +0200
     3.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle.properties	Mon Sep 28 14:42:19 2009 +0200
     3.3 @@ -57,6 +57,7 @@
     3.4  MOVES=Moves
     3.5  ROOT=Home
     3.6  
     3.7 +TALK=Comments
     3.8  
     3.9  NAME=Name
    3.10  PASSWORD=Password
     4.1 --- a/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle_cs.properties	Sat Sep 26 21:47:02 2009 +0200
     4.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/Bundle_cs.properties	Mon Sep 28 14:42:19 2009 +0200
     4.3 @@ -72,6 +72,8 @@
     4.4    5#zb\u00FDv\u00E1 {0} pl\u016Ftk\u016F\
     4.5  })
     4.6  
     4.7 +TALK=Koment\u00E1\u0159e
     4.8 +
     4.9  NAME=Jm\u00E9no
    4.10  PASSWORD=Heslo
    4.11  LOGIN=P\u0159ihl\u00E1sit se
     5.1 --- a/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/game.fmt	Sat Sep 26 21:47:02 2009 +0200
     5.2 +++ b/freemarkerdor/src/main/resources/cz/xelfi/quoridor/freemarkerdor/UI/game.fmt	Mon Sep 28 14:42:19 2009 +0200
     5.3 @@ -237,6 +237,20 @@
     5.4                </#list>
     5.5            </tbody>
     5.6        </table>
     5.7 +
     5.8 +      <h3><a href="/games/${doc.game.id.@id}?move=0">${bundle.TALK}</a></h3>
     5.9 +
    5.10 +      <ul>
    5.11 +      <#list doc.game.moves.item.comment as item>
    5.12 +        <li><b>${item.@who}</b>: ${item}</li>
    5.13 +      </#list>
    5.14 +      </ul>
    5.15 +
    5.16 +      <form action="/games/${doc.game.id.@id}/comment">
    5.17 +          <textarea rows="2" cols="80" name="comment"></textarea>
    5.18 +          <input type="submit"/>
    5.19 +      </form>
    5.20 +
    5.21        <hr/>
    5.22        ${bundle("copyright", version)}
    5.23    </body>
     6.1 --- a/webidor/pom.xml	Sat Sep 26 21:47:02 2009 +0200
     6.2 +++ b/webidor/pom.xml	Mon Sep 28 14:42:19 2009 +0200
     6.3 @@ -9,7 +9,7 @@
     6.4    <groupId>org.apidesign</groupId>
     6.5    <artifactId>webidor</artifactId>
     6.6    <packaging>jar</packaging>
     6.7 -  <version>1.3</version>
     6.8 +  <version>1.4</version>
     6.9    <name>webidor server</name>
    6.10    <url>http://maven.apache.org</url>
    6.11    <repositories>
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/webidor/src/main/java/cz/xelfi/quoridor/webidor/CommentedMove.java	Mon Sep 28 14:42:19 2009 +0200
     7.3 @@ -0,0 +1,104 @@
     7.4 +package cz.xelfi.quoridor.webidor;
     7.5 +
     7.6 +import cz.xelfi.quoridor.IllegalPositionException;
     7.7 +import cz.xelfi.quoridor.Move;
     7.8 +import java.util.ArrayList;
     7.9 +import java.util.List;
    7.10 +import javax.xml.bind.annotation.XmlAttribute;
    7.11 +import javax.xml.bind.annotation.XmlElement;
    7.12 +
    7.13 +public final class CommentedMove {
    7.14 +    Move move;
    7.15 +    int index;
    7.16 +    List<Note> comments;
    7.17 +
    7.18 +    private CommentedMove() {
    7.19 +        super();
    7.20 +    }
    7.21 +
    7.22 +    CommentedMove(Move move, int index) {
    7.23 +        super();
    7.24 +        this.move = move;
    7.25 +        this.index = index;
    7.26 +    }
    7.27 +
    7.28 +    public Move getMove() {
    7.29 +        return move;
    7.30 +    }
    7.31 +
    7.32 +    @XmlAttribute(name="move")
    7.33 +    String getMoveText() {
    7.34 +        return move.toString();
    7.35 +    }
    7.36 +
    7.37 +    void setMoveText(String m) throws IllegalPositionException {
    7.38 +        move = Move.valueOf(m);
    7.39 +    }
    7.40 +    
    7.41 +    @XmlAttribute
    7.42 +    int getIndex() {
    7.43 +        return index;
    7.44 +    }
    7.45 +
    7.46 +    void setIndex(int i) {
    7.47 +        index = i;
    7.48 +    }
    7.49 +
    7.50 +    void addNote(Note n) {
    7.51 +        if (comments == null) {
    7.52 +            comments = new ArrayList<Note>();
    7.53 +        }
    7.54 +        comments.add(n);
    7.55 +    }
    7.56 +
    7.57 +    @XmlElement(name="comment", required=false)
    7.58 +    public final List<Note> getComments() {
    7.59 +        return comments;
    7.60 +    }
    7.61 +
    7.62 +    final void setComments(List<Note> l) {
    7.63 +        this.comments = l;
    7.64 +    }
    7.65 +
    7.66 +    @Override
    7.67 +    public String toString() {
    7.68 +        StringBuilder sb = new StringBuilder();
    7.69 +        sb.append("Move[").append(move).append("@").append(index);
    7.70 +        if (comments != null) {
    7.71 +            sb.append(", ");
    7.72 +            sb.append(comments);
    7.73 +        }
    7.74 +        sb.append("]");
    7.75 +        return sb.toString();
    7.76 +    }
    7.77 +
    7.78 +    @Override
    7.79 +    public boolean equals(Object obj) {
    7.80 +        if (obj == null) {
    7.81 +            return false;
    7.82 +        }
    7.83 +        if (getClass() != obj.getClass()) {
    7.84 +            return false;
    7.85 +        }
    7.86 +        final CommentedMove other = (CommentedMove) obj;
    7.87 +        if (this.move != other.move && (this.move == null || !this.move.equals(other.move))) {
    7.88 +            return false;
    7.89 +        }
    7.90 +        if (this.index != other.index) {
    7.91 +            return false;
    7.92 +        }
    7.93 +        if (this.comments != other.comments && (this.comments == null || !this.comments.equals(other.comments))) {
    7.94 +            return false;
    7.95 +        }
    7.96 +        return true;
    7.97 +    }
    7.98 +
    7.99 +    @Override
   7.100 +    public int hashCode() {
   7.101 +        int hash = 5;
   7.102 +        hash = 43 * hash + (this.move != null ? this.move.hashCode() : 0);
   7.103 +        hash = 43 * hash + this.index;
   7.104 +        hash = 43 * hash + (this.comments != null ? this.comments.hashCode() : 0);
   7.105 +        return hash;
   7.106 +    }
   7.107 +}
     8.1 --- a/webidor/src/main/java/cz/xelfi/quoridor/webidor/Game.java	Sat Sep 26 21:47:02 2009 +0200
     8.2 +++ b/webidor/src/main/java/cz/xelfi/quoridor/webidor/Game.java	Mon Sep 28 14:42:19 2009 +0200
     8.3 @@ -37,6 +37,7 @@
     8.4  import javax.xml.bind.annotation.XmlAccessorType;
     8.5  import javax.xml.bind.annotation.XmlAttribute;
     8.6  import javax.xml.bind.annotation.XmlElement;
     8.7 +import javax.xml.bind.annotation.XmlElementWrapper;
     8.8  import javax.xml.bind.annotation.XmlRootElement;
     8.9  import javax.xml.bind.annotation.adapters.XmlAdapter;
    8.10  import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
    8.11 @@ -53,9 +54,9 @@
    8.12      @XmlElement
    8.13      @XmlJavaTypeAdapter(BoardAdapter.class)
    8.14      private Board board;
    8.15 -    @XmlElement
    8.16 -    @XmlJavaTypeAdapter(MoveAdapter.class)
    8.17 -    private List<Move> moves;
    8.18 +    @XmlElementWrapper(name="moves")
    8.19 +    @XmlElement(name="item")
    8.20 +    private List<CommentedMove> moves;
    8.21      private Integer move;
    8.22  
    8.23      Game() {
    8.24 @@ -106,12 +107,18 @@
    8.25              when = new Date(id.getModified());
    8.26          }
    8.27          id = new GameId(id.getId(), id.getWhite(), id.getBlack(), new Date(id.getStarted()), when, GameStatus.valueOf(board));
    8.28 -        getMoves().add(m);
    8.29 +        getMoves().add(new CommentedMove(m, getMoves().size() + 1));
    8.30      }
    8.31  
    8.32 -    public List<Move> getMoves() {
    8.33 +    public void comment(String player, String comment, Date date) {
    8.34 +        Note n = new Note(comment, date, player);
    8.35 +        getMoves().get(getMoves().size() - 1).addNote(n);
    8.36 +    }
    8.37 +
    8.38 +
    8.39 +    public List<CommentedMove> getMoves() {
    8.40          if (moves == null) {
    8.41 -            moves = new ArrayList<Move>();
    8.42 +            moves = new ArrayList<CommentedMove>();
    8.43          }
    8.44          return moves;
    8.45      }
    8.46 @@ -124,11 +131,11 @@
    8.47      public Game snapshot(int move) throws IllegalPositionException {
    8.48          Board b = Board.empty();
    8.49          int cnt = 0;
    8.50 -        for (Move m : getMoves()) {
    8.51 +        for (CommentedMove m : getMoves()) {
    8.52              if (move-- <= 0) {
    8.53                  break;
    8.54              }
    8.55 -            b = b.apply(m);
    8.56 +            b = b.apply(m.getMove());
    8.57              cnt++;
    8.58          }
    8.59  
    8.60 @@ -141,32 +148,16 @@
    8.61          );
    8.62          g.board = b;
    8.63          g.move = cnt;
    8.64 -        g.moves = new ArrayList<Move>(getMoves());
    8.65 +        g.moves = new ArrayList<CommentedMove>(getMoves());
    8.66          return g;
    8.67      }
    8.68 -
    8.69 -    @XmlAccessorType(XmlAccessType.FIELD)
    8.70 -    private static final class MoveTmp {
    8.71 -        @XmlAttribute
    8.72 -        String move;
    8.73 -        @XmlAttribute
    8.74 -        int index;
    8.75 -
    8.76 -        private MoveTmp() {
    8.77 -        }
    8.78 -
    8.79 -        public MoveTmp(String move, int index) {
    8.80 -            this.move = move;
    8.81 -            this.index = index;
    8.82 -        }
    8.83 -    }
    8.84 -
    8.85 -    private static final class MoveAdapter extends XmlAdapter<MoveTmp[],List<Move>> {
    8.86 +/*
    8.87 +    private static final class MoveAdapter extends XmlAdapter<CommentedMove[],List<Move>> {
    8.88          @Override
    8.89 -        public List<Move> unmarshal(MoveTmp[] arr) throws Exception {
    8.90 +        public List<Move> unmarshal(CommentedMove[] arr) throws Exception {
    8.91              List<Move> res = new ArrayList<Move>();
    8.92              if (arr != null) {
    8.93 -                for (MoveTmp v : arr) {
    8.94 +                for (CommentedMove v : arr) {
    8.95                      res.add(Move.valueOf(v.move));
    8.96                  }
    8.97              }
    8.98 @@ -174,18 +165,18 @@
    8.99          }
   8.100  
   8.101          @Override
   8.102 -        public MoveTmp[] marshal(List<Move> arr) throws Exception {
   8.103 -            List<MoveTmp> res = new ArrayList<MoveTmp>();
   8.104 +        public CommentedMove[] marshal(List<Move> arr) throws Exception {
   8.105 +            List<CommentedMove> res = new ArrayList<CommentedMove>();
   8.106              if (arr != null) {
   8.107                  int index = 0;
   8.108                  for (Move m : arr) {
   8.109 -                    res.add(new MoveTmp(m.toString(), ++index));
   8.110 +                    res.add(new CommentedMove(m.toString(), ++index));
   8.111                  }
   8.112              }
   8.113 -            return res.toArray(new MoveTmp[0]);
   8.114 +            return res.toArray(new CommentedMove[0]);
   8.115          }
   8.116      } // end of MoveAdapter
   8.117 -
   8.118 +*/
   8.119      private static final class BoardAdapter extends XmlAdapter<String,Board> {
   8.120  
   8.121          @Override
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/webidor/src/main/java/cz/xelfi/quoridor/webidor/Note.java	Mon Sep 28 14:42:19 2009 +0200
     9.3 @@ -0,0 +1,79 @@
     9.4 +package cz.xelfi.quoridor.webidor;
     9.5 +
     9.6 +import java.util.Date;
     9.7 +import javax.xml.bind.annotation.XmlAccessType;
     9.8 +import javax.xml.bind.annotation.XmlAccessorType;
     9.9 +import javax.xml.bind.annotation.XmlAttribute;
    9.10 +import javax.xml.bind.annotation.XmlValue;
    9.11 +
    9.12 +/**
    9.13 + *
    9.14 + * @author Jaroslav Tulach <jtulach@netbeans.org>
    9.15 + */
    9.16 +@XmlAccessorType(XmlAccessType.FIELD)
    9.17 +public class Note {
    9.18 +    @XmlValue
    9.19 +    private String comment;
    9.20 +    @XmlAttribute
    9.21 +    private Date when;
    9.22 +    @XmlAttribute
    9.23 +    private String who;
    9.24 +
    9.25 +    private Note() {
    9.26 +    }
    9.27 +
    9.28 +    public Note(String comment, Date when, String who) {
    9.29 +        this.comment = comment;
    9.30 +        this.when = new Date(when.getTime());
    9.31 +        this.who = who;
    9.32 +    }
    9.33 +
    9.34 +    public String getComment() {
    9.35 +        return comment;
    9.36 +    }
    9.37 +
    9.38 +    public Date getWhen() {
    9.39 +        return (Date) when.clone();
    9.40 +    }
    9.41 +
    9.42 +    public String getWho() {
    9.43 +        return who;
    9.44 +    }
    9.45 +
    9.46 +    @Override
    9.47 +    public String toString() {
    9.48 +        return "Note[" + who + "@" + when + ": " + comment + "]";
    9.49 +    }
    9.50 +
    9.51 +    @Override
    9.52 +    public boolean equals(Object obj) {
    9.53 +        if (obj == null) {
    9.54 +            return false;
    9.55 +        }
    9.56 +        if (getClass() != obj.getClass()) {
    9.57 +            return false;
    9.58 +        }
    9.59 +        final Note other = (Note) obj;
    9.60 +        if ((this.comment == null) ? (other.comment != null) : !this.comment.equals(other.comment)) {
    9.61 +            return false;
    9.62 +        }
    9.63 +        if (this.when != other.when && (this.when == null || !this.when.equals(other.when))) {
    9.64 +            return false;
    9.65 +        }
    9.66 +        if ((this.who == null) ? (other.who != null) : !this.who.equals(other.who)) {
    9.67 +            return false;
    9.68 +        }
    9.69 +        return true;
    9.70 +    }
    9.71 +
    9.72 +    @Override
    9.73 +    public int hashCode() {
    9.74 +        int hash = 7;
    9.75 +        hash = 59 * hash + (this.comment != null ? this.comment.hashCode() : 0);
    9.76 +        hash = 59 * hash + (this.when != null ? this.when.hashCode() : 0);
    9.77 +        hash = 59 * hash + (this.who != null ? this.who.hashCode() : 0);
    9.78 +        return hash;
    9.79 +    }
    9.80 +
    9.81 +
    9.82 +}
    10.1 --- a/webidor/src/main/java/cz/xelfi/quoridor/webidor/resources/Games.java	Sat Sep 26 21:47:02 2009 +0200
    10.2 +++ b/webidor/src/main/java/cz/xelfi/quoridor/webidor/resources/Games.java	Mon Sep 28 14:42:19 2009 +0200
    10.3 @@ -42,6 +42,8 @@
    10.4  import java.util.List;
    10.5  import java.util.logging.Level;
    10.6  import java.util.logging.Logger;
    10.7 +import java.util.regex.Matcher;
    10.8 +import java.util.regex.Pattern;
    10.9  import javax.ws.rs.DefaultValue;
   10.10  import javax.ws.rs.GET;
   10.11  import javax.ws.rs.POST;
   10.12 @@ -138,7 +140,8 @@
   10.13          @QueryParam("loginID") String loginId,
   10.14          @PathParam("id") String id,
   10.15          @QueryParam("player") String player,
   10.16 -        @QueryParam("move") String move
   10.17 +        @QueryParam("move") String move,
   10.18 +        @QueryParam("comment") String comment
   10.19      ) throws IllegalPositionException {
   10.20          String logUser = quoridor.isLoggedIn(loginId);
   10.21          if (logUser == null) {
   10.22 @@ -147,13 +150,21 @@
   10.23          if (!logUser.equals(player)) {
   10.24              throw new WebApplicationException(Status.UNAUTHORIZED);
   10.25          }
   10.26 +        if (comment == null && move == null) {
   10.27 +            throw new WebApplicationException(Status.BAD_REQUEST);
   10.28 +        }
   10.29  
   10.30          Game g = findGame(id);
   10.31          if (g == null) {
   10.32              throw new IllegalArgumentException("Unknown game " + id);
   10.33          }
   10.34 -        Move m = Move.valueOf(move);
   10.35 -        g.apply(player, m, new Date());
   10.36 +        if (move != null) {
   10.37 +            Move m = Move.valueOf(move);
   10.38 +            g.apply(player, m, new Date());
   10.39 +        }
   10.40 +        if (comment != null) {
   10.41 +            g.comment(player, comment, new Date());
   10.42 +        }
   10.43          try {
   10.44              storeGame(g);
   10.45          } catch (IOException ex) {
   10.46 @@ -198,11 +209,15 @@
   10.47          }
   10.48      }
   10.49  
   10.50 +    private static final Pattern saidWho = Pattern.compile("# *([^ ]*) *@(.*):$");
   10.51 +
   10.52      private Game readGame(File f) throws IOException {
   10.53          BufferedReader r = new BufferedReader(new FileReader(f));
   10.54          String white = null;
   10.55          String black = null;
   10.56          Game g = null;
   10.57 +        String who = null;
   10.58 +        String when = null;
   10.59          for (;;) {
   10.60              String line = r.readLine();
   10.61              if (line == null) {
   10.62 @@ -221,6 +236,31 @@
   10.63                  continue;
   10.64              }
   10.65              if (line.startsWith("#")) {
   10.66 +                Matcher m =saidWho.matcher(line);
   10.67 +                if (m.matches()) {
   10.68 +                    who = m.group(1);
   10.69 +                    when = m.group(2);
   10.70 +                    continue;
   10.71 +                }
   10.72 +                if (g == null) {
   10.73 +                    continue;
   10.74 +                }
   10.75 +                if (line.startsWith("# ")) {
   10.76 +                    line = line.substring(2);
   10.77 +                } else {
   10.78 +                    line = line.substring(1);
   10.79 +                }
   10.80 +                Date d = new Date();
   10.81 +                try {
   10.82 +                    if (when != null) {
   10.83 +                        d = new Date(Date.parse(when));
   10.84 +                    }
   10.85 +                } catch (IllegalArgumentException ex) {
   10.86 +                    LOG.warning("Unparseable date " + when + " in " + f);
   10.87 +                }
   10.88 +                g.comment(who, line, d);
   10.89 +                who = null;
   10.90 +                when = null;
   10.91                  continue;
   10.92              }
   10.93              if (white == null || black == null) {
   10.94 @@ -264,17 +304,47 @@
   10.95      private void storeGame(Game g) throws IOException {
   10.96          dir.mkdirs();
   10.97          File f = new File(dir, g.getId().getId());
   10.98 +        storeGame(g, f);
   10.99 +    }
  10.100 +
  10.101 +    final void storeGame(Game g, File f) throws IOException {
  10.102          PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(f)));
  10.103          pw.println("# white: " + g.getId().getWhite());
  10.104          pw.println("# black: " + g.getId().getBlack());
  10.105          pw.println("# status: " + g.getId().getStatus());
  10.106          int cnt = 0;
  10.107 -        for (Move m : g.getMoves()) {
  10.108 -            pw.print(m.toString());
  10.109 -            if (++cnt % 2 == 0) {
  10.110 +        boolean separate = true;
  10.111 +        for (CommentedMove m : g.getMoves()) {
  10.112 +            if (!separate && cnt % 2 == 1) {
  10.113 +                pw.print("... ");
  10.114 +            }
  10.115 +            separate = true;
  10.116 +            pw.print(m.getMove().toString());
  10.117 +            List<Note> notes = m.getComments();
  10.118 +            if (notes != null) {
  10.119 +                separate = false;
  10.120                  pw.println();
  10.121 -            } else {
  10.122 -                pw.print(' ');
  10.123 +                for (Note n : notes) {
  10.124 +                    pw.print ("# ");
  10.125 +                    pw.print(n.getWho());
  10.126 +                    pw.print("@");
  10.127 +                    pw.print(n.getWhen());
  10.128 +                    pw.println(":");
  10.129 +                    for (String l : n.getComment().split("\n")) {
  10.130 +                        pw.print("# ");
  10.131 +                        pw.println(l);
  10.132 +                    }
  10.133 +                }
  10.134 +            }
  10.135 +
  10.136 +            cnt++;
  10.137 +
  10.138 +            if (separate) {
  10.139 +                if (cnt % 2 == 0) {
  10.140 +                    pw.println();
  10.141 +                } else {
  10.142 +                    pw.print(' ');
  10.143 +                }
  10.144              }
  10.145          }
  10.146          pw.println();
    11.1 --- a/webidor/src/test/java/cz/xelfi/quoridor/webidor/QuoridorTest.java	Sat Sep 26 21:47:02 2009 +0200
    11.2 +++ b/webidor/src/test/java/cz/xelfi/quoridor/webidor/QuoridorTest.java	Mon Sep 28 14:42:19 2009 +0200
    11.3 @@ -162,10 +162,13 @@
    11.4              fail("The game is newly modified");
    11.5          }
    11.6          Game snapshot = webResource.path("games/" + s.getId()).queryParam("move", "0").accept(MediaType.TEXT_XML).get(Game.class);
    11.7 -        assertEquals("All moves listed", 2, snapshot.getMoves().size());
    11.8 +        String ssnapshot = webResource.path("games/" + s.getId()).queryParam("move", "0").accept(MediaType.TEXT_XML).get(String.class);
    11.9 +        assertEquals("All moves listed:\n" + ssnapshot, 2, snapshot.getMoves().size());
   11.10          assertEquals("Current move", 0, snapshot.getCurrentMove());
   11.11          assertEquals("Position 0", 0, snapshot.getBoard().getPlayers().get(0).getRow());
   11.12          assertEquals("Position 8", 8, snapshot.getBoard().getPlayers().get(1).getRow());
   11.13 +        assertEquals("Moves numbered from 1", 1, snapshot.getMoves().get(0).getIndex());
   11.14 +        assertEquals("Moves numbered from 1, 2", 2, snapshot.getMoves().get(1).getIndex());
   11.15  
   11.16          File game = new File(new File(dir, "games"), s2.getId());
   11.17          assertTrue("File for game exists", game.exists());
   11.18 @@ -191,8 +194,8 @@
   11.19          Board board = readGames.get(0).getBoard();
   11.20          assertEquals(1, board.getPlayers().get(0).getRow());
   11.21          assertEquals(7, board.getPlayers().get(1).getRow());
   11.22 -        assertEquals(Move.NORTH, readGames.get(0).getMoves().get(0));
   11.23 -        assertEquals(Move.SOUTH, readGames.get(0).getMoves().get(1));
   11.24 +        assertEquals(Move.NORTH, readGames.get(0).getMoves().get(0).getMove());
   11.25 +        assertEquals(Move.SOUTH, readGames.get(0).getMoves().get(1).getMove());
   11.26  
   11.27          class GMap extends GenericType<Map<String,Object>>{}
   11.28          String text = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_PLAIN).get(String.class);
   11.29 @@ -200,12 +203,13 @@
   11.30              fail("Expecting board:\n" + text);
   11.31          }
   11.32          Game readGame = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_XML).get(Game.class);
   11.33 +        String sGame = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_XML).get(String.class);
   11.34          assertNotNull("Game really returned", readGame);
   11.35          assertEquals("Same game as in text representation", readGame.getBoard(), Board.valueOf(text));
   11.36          assertEquals("It is same as text of our game", readGame.getBoard().toString(), text);
   11.37  
   11.38 -        assertEquals(Move.NORTH, readGame.getMoves().get(0));
   11.39 -        assertEquals(Move.SOUTH, readGame.getMoves().get(1));
   11.40 +        assertEquals(Move.NORTH, readGame.getMoves().get(0).getMove());
   11.41 +        assertEquals(Move.SOUTH, readGame.getMoves().get(1).getMove());
   11.42      }
   11.43  
   11.44  }
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/webidor/src/test/java/cz/xelfi/quoridor/webidor/resources/ChatTest.java	Mon Sep 28 14:42:19 2009 +0200
    12.3 @@ -0,0 +1,205 @@
    12.4 +/*
    12.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    12.6 + *
    12.7 + * The contents of this file are subject to the terms of either the GNU
    12.8 + * General Public License Version 2 only ("GPL") or the Common
    12.9 + * Development and Distribution License("CDDL") (collectively, the
   12.10 + * "License"). You may not use this file except in compliance with the
   12.11 + * License. You can obtain a copy of the License at
   12.12 + * http://www.netbeans.org/cddl-gplv2.html
   12.13 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
   12.14 + * specific language governing permissions and limitations under the
   12.15 + * License.  When distributing the software, include this License Header
   12.16 + * Notice in each file and include the License file at
   12.17 + * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
   12.18 + * particular file as subject to the "Classpath" exception as provided
   12.19 + * by Sun in the GPL Version 2 section of the License file that
   12.20 + * accompanied this code. If applicable, add the following below the
   12.21 + * License Header, with the fields enclosed by brackets [] replaced by
   12.22 + * your own identifying information:
   12.23 + * "Portions Copyrighted [year] [name of copyright owner]"
   12.24 + *
   12.25 + * Contributor(s):
   12.26 + *
   12.27 + * Portions Copyrighted 2009 Jaroslav Tulach
   12.28 + */
   12.29 +
   12.30 +package cz.xelfi.quoridor.webidor.resources;
   12.31 +
   12.32 +import com.sun.jersey.test.framework.JerseyTest;
   12.33 +import cz.xelfi.quoridor.Board;
   12.34 +import cz.xelfi.quoridor.Move;
   12.35 +import cz.xelfi.quoridor.webidor.Game;
   12.36 +import cz.xelfi.quoridor.webidor.GameId;
   12.37 +import cz.xelfi.quoridor.webidor.Note;
   12.38 +import cz.xelfi.quoridor.webidor.QuoridorTest;
   12.39 +import java.io.File;
   12.40 +import java.io.FileNotFoundException;
   12.41 +import java.io.FileOutputStream;
   12.42 +import java.io.FileReader;
   12.43 +import java.io.IOException;
   12.44 +import java.util.List;
   12.45 +import javax.ws.rs.core.MediaType;
   12.46 +import org.junit.Test;
   12.47 +import static org.junit.Assert.*;
   12.48 +
   12.49 +/**
   12.50 + *
   12.51 + * @author Jaroslav Tulach <jtulach@netbeans.org>
   12.52 + */
   12.53 +public class ChatTest extends JerseyTest {
   12.54 +    private File dir;
   12.55 +
   12.56 +    public ChatTest() throws Exception {
   12.57 +        super("cz.xelfi.quoridor.webidor.resources");
   12.58 +    }
   12.59 +
   12.60 +    @Override
   12.61 +    public void setUp() throws Exception {
   12.62 +        dir = File.createTempFile("quoridor", ".dir");
   12.63 +        dir.delete();
   12.64 +        System.setProperty("quoridor.dir", dir.getPath());
   12.65 +        dir.mkdirs();
   12.66 +        File passwd = new File(dir, "passwd");
   12.67 +        FileOutputStream os = new FileOutputStream(passwd);
   12.68 +        os.write("Jarda=heslo\nJirka=pesko\n".getBytes("UTF-8"));
   12.69 +        os.close();
   12.70 +        super.setUp();
   12.71 +    }
   12.72 +
   12.73 +    @Override
   12.74 +    public void tearDown() throws Exception {
   12.75 +        super.tearDown();
   12.76 +        deleteRec(dir);
   12.77 +    }
   12.78 +
   12.79 +    static void deleteRec(File dir) throws IOException {
   12.80 +        if (dir == null) {
   12.81 +            return;
   12.82 +        }
   12.83 +        File[] arr = dir.listFiles();
   12.84 +        if (arr != null) {
   12.85 +            for (File f : arr) {
   12.86 +                deleteRec(f);
   12.87 +            }
   12.88 +        }
   12.89 +        dir.delete();
   12.90 +    }
   12.91 +
   12.92 +    @Test public void testCreateAGame() throws Exception {
   12.93 +        webResource = webResource.path("api");
   12.94 +        String logJarda = webResource.path("login").
   12.95 +            queryParam("name", "Jarda").
   12.96 +            queryParam("password", "heslo").
   12.97 +            accept(MediaType.TEXT_PLAIN).
   12.98 +            put(String.class);
   12.99 +        String logJirka = webResource.path("login").
  12.100 +            queryParam("name", "Jirka").
  12.101 +            queryParam("password", "pesko").
  12.102 +            accept(MediaType.TEXT_PLAIN).
  12.103 +            put(String.class);
  12.104 +    GameId s = webResource.path("games").queryParam("loginID", logJarda).
  12.105 +                queryParam("white", "Jarda")
  12.106 +                .queryParam("black", "Jirka").post(GameId.class);
  12.107 +
  12.108 +        Thread.sleep(100);
  12.109 +        final long now = System.currentTimeMillis();
  12.110 +        if (s.getModified() >= now) {
  12.111 +            fail("The game is supposed to be modified in past");
  12.112 +        }
  12.113 +        Thread.sleep(100);
  12.114 +
  12.115 +        GameId s1 = webResource.path("games/" + s.getId()).
  12.116 +            queryParam("loginID", logJarda).
  12.117 +            queryParam("player", "Jarda").queryParam("move", "N").put(GameId.class);
  12.118 +
  12.119 +        GameId comment1 = webResource.path("games/" + s.getId()).
  12.120 +            queryParam("loginID", logJarda).
  12.121 +            queryParam("player", "Jarda").queryParam("comment", "I like this game!").put(GameId.class);
  12.122 +
  12.123 +        GameId comment2 = webResource.path("games/" + s.getId()).
  12.124 +            queryParam("loginID", logJirka).
  12.125 +            queryParam("player", "Jirka").queryParam("comment", "I love it too!").put(GameId.class);
  12.126 +        GameId s2 = webResource.path("games/" + s.getId()).
  12.127 +            queryParam("loginID", logJirka).
  12.128 +            queryParam("player", "Jirka").queryParam("move", "S").put(GameId.class);
  12.129 +        assertNotNull("Successful move", s2);
  12.130 +
  12.131 +        File game = new File(new File(dir, "games"), s1.getId());
  12.132 +        assertTrue("File for game exists", game.exists());
  12.133 +        String content = readFile(game);
  12.134 +
  12.135 +        if (!content.contains("# white: Jarda")) {
  12.136 +            fail(content);
  12.137 +        }
  12.138 +        if (!content.contains("# black: Jirka")) {
  12.139 +            fail(content);
  12.140 +        }
  12.141 +        if (!content.contains("N")) {
  12.142 +            fail(content);
  12.143 +        }
  12.144 +        if (!content.contains("... S")) {
  12.145 +            fail(content);
  12.146 +        }
  12.147 +        if (!content.contains("I like")) {
  12.148 +            fail(content);
  12.149 +        }
  12.150 +        if (!content.contains("I love")) {
  12.151 +            fail(content);
  12.152 +        }
  12.153 +
  12.154 +        Games read = new Games(new File(dir, "games"), new Quoridor());
  12.155 +        List<Game> readGames = read.getGames();
  12.156 +        assertEquals("One game read", 1, readGames.size());
  12.157 +        Board board = readGames.get(0).getBoard();
  12.158 +        assertEquals(1, board.getPlayers().get(0).getRow());
  12.159 +        assertEquals(7, board.getPlayers().get(1).getRow());
  12.160 +        assertEquals(Move.NORTH, readGames.get(0).getMoves().get(0).getMove());
  12.161 +        assertEquals(Move.SOUTH, readGames.get(0).getMoves().get(1).getMove());
  12.162 +
  12.163 +        Game rg = readGames.get(0);
  12.164 +        assertNull("No comments on second move", rg.getMoves().get(1).getComments());
  12.165 +        List<Note> cmnts = rg.getMoves().get(0).getComments();
  12.166 +        assertNotNull("Some comments on first move", cmnts);
  12.167 +        assertEquals("Two comments: " + cmnts, 2, cmnts.size());
  12.168 +
  12.169 +        if (!cmnts.get(0).getComment().contains("I like")) {
  12.170 +            fail();
  12.171 +        }
  12.172 +        if (!cmnts.get(1).getComment().contains("I love")) {
  12.173 +            fail();
  12.174 +        }
  12.175 +
  12.176 +        File tmp = File.createTempFile("test-board", "game");
  12.177 +        read.storeGame(rg, tmp);
  12.178 +
  12.179 +        assertEquals("Newly written file remains the same", readFile(game), readFile(tmp));
  12.180 +
  12.181 +        String sGame = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_XML).get(String.class);
  12.182 +        Game readGame = webResource.path("games").path(s.getId()).accept(MediaType.TEXT_XML).get(Game.class);
  12.183 +        assertNotNull("Game really returned", readGame);
  12.184 +
  12.185 +        assertEquals(Move.NORTH, readGame.getMoves().get(0).getMove());
  12.186 +        assertEquals(Move.SOUTH, readGame.getMoves().get(1).getMove());
  12.187 +
  12.188 +        assertNull("No comments on second move", readGame.getMoves().get(1).getComments());
  12.189 +        cmnts = readGame.getMoves().get(0).getComments();
  12.190 +        assertNotNull("Some comments on first move", cmnts);
  12.191 +        assertEquals("Two comments: " + cmnts, 2, cmnts.size());
  12.192 +        if (!cmnts.get(0).getComment().contains("I like")) {
  12.193 +            fail();
  12.194 +        }
  12.195 +        if (!cmnts.get(1).getComment().contains("I love")) {
  12.196 +            fail();
  12.197 +        }
  12.198 +    }
  12.199 +
  12.200 +    private String readFile(File game) throws IOException, FileNotFoundException {
  12.201 +        char[] arr = new char[4096];
  12.202 +        FileReader gameContent = new FileReader(game);
  12.203 +        int len = gameContent.read(arr);
  12.204 +        String content = new String(arr, 0, len);
  12.205 +        return content;
  12.206 +    }
  12.207 +
  12.208 +}