[LIB-9] Add reserved, NAG and semicolon comment PGN tokens format
authorgotty <gotty@hedgecode.org>
Mon, 27 Jan 2020 02:49:26 +0000 (05:49 +0300)
committergotty <gotty@hedgecode.org>
Mon, 27 Jan 2020 02:49:26 +0000 (05:49 +0300)
chesshog-format/src/main/java/org/hedgecode/chess/pgn/PGNUtils.java
chesshog-format/src/main/java/org/hedgecode/chess/pgn/token/CommentToken.java
chesshog-format/src/main/java/org/hedgecode/chess/pgn/token/MoveToken.java
chesshog-format/src/main/java/org/hedgecode/chess/pgn/token/MovesToken.java
chesshog-format/src/main/java/org/hedgecode/chess/pgn/token/NAGToken.java [new file with mode: 0644]
chesshog-format/src/main/java/org/hedgecode/chess/pgn/token/ReservedToken.java [new file with mode: 0644]
chesshog-format/src/main/resources/org/hedgecode/chess/LocalStrings.properties
chesshog-format/src/main/resources/org/hedgecode/chess/LocalStrings_ru.properties

index 884a388..ae5c046 100644 (file)
@@ -32,6 +32,7 @@ public final class PGNUtils {
     private static final String SHIELD_REGEX = "\\\\";
 
     private static final String EMPTY = "";
+    private static final String TAB = "\t";
     private static final String CRLF_REGEX = "\\r?\\n";
 
     public static String match(String source, String regex) {
@@ -82,6 +83,10 @@ public final class PGNUtils {
         return pgn.replaceAll(CRLF_REGEX, PGN_SPACE);
     }
 
+    public static String tabCrlf(String pgn) {
+        return pgn.replaceAll(TAB, PGN_SPACE).replaceAll(CRLF_REGEX, TAB);
+    }
+
     private PGNUtils() {
         throw new AssertionError(
                 String.format("No %s instances!", getClass().getName())
index 59feecb..22e5a0a 100644 (file)
@@ -29,13 +29,37 @@ import org.hedgecode.chess.pgn.entity.Moves;
  */
 public class CommentToken implements Token<Moves> {
 
-    private static final String COMMENT_REGEX = "^\\s*\\{([^}]+)\\}";
-    private static final Pattern COMMENT_PATTERN = Pattern.compile(COMMENT_REGEX);
+    enum Type {
+
+        BRACE     ( BRACE_PATTERN     ),
+        SEMICOLON ( SEMICOLON_PATTERN );
+
+        private Pattern pattern;
+
+        Type(Pattern pattern) {
+            this.pattern = pattern;
+        }
+
+        public Pattern pattern() {
+            return pattern;
+        }
+    }
+
+    private static final String BRACE_REGEX = "^\\s*\\{([^}]+)\\}";
+    private static final Pattern BRACE_PATTERN = Pattern.compile(BRACE_REGEX);
+    private static final String SEMICOLON_REGEX = "^\\s*;([^\t]+)\t";
+    private static final Pattern SEMICOLON_PATTERN = Pattern.compile(SEMICOLON_REGEX);
     private static final int COMMENT_GROUP = 1;
 
+    private Type commentType;
+
+    public CommentToken(Type type) {
+        this.commentType = type;
+    }
+
     @Override
     public int token(Moves moves, String pgn) throws ParseException {
-        Matcher matcher = COMMENT_PATTERN.matcher(pgn);
+        Matcher matcher = commentType.pattern().matcher(pgn);
         if (!matcher.find()) {
             throw new ParseException("parse.pgn.incorrect.comment", pgn);
         } else {
index ea89dfc..0191ad4 100644 (file)
@@ -23,7 +23,6 @@ import org.hedgecode.chess.ParseException;
 import org.hedgecode.chess.pgn.entity.DetailMove;
 import org.hedgecode.chess.pgn.entity.MoveNumber;
 import org.hedgecode.chess.pgn.entity.Moves;
-import org.hedgecode.chess.pgn.nag.Glyph;
 import org.hedgecode.chess.pgn.token.san.CommonSANToken;
 import org.hedgecode.chess.pgn.token.san.SANToken;
 
@@ -34,7 +33,7 @@ import org.hedgecode.chess.pgn.token.san.SANToken;
  */
 public class MoveToken implements Token<Moves> {
 
-    public static final String MOVE_REGEX = "^\\s*([PNBRQKa-hx1-8=+#\\-O!?]+)(\\s+\\$([0-9]+))?";
+    public static final String MOVE_REGEX = "^\\s*([PNBRQKa-hx1-8=+#\\-O!?]+)";
     private static final Pattern MOVE_PATTERN = Pattern.compile(MOVE_REGEX);
     private static final int MOVE_GROUP = 1, NAG_GROUP = 3;
 
@@ -46,9 +45,9 @@ public class MoveToken implements Token<Moves> {
         MoveNumber moveNumber = new MoveNumber(
                 moves.currentMove().ply()
         );
-        int numberLength = moveNumberToken.token(moveNumber, pgn);
-        if (numberLength > 0) {
-            pgn = pgn.substring(numberLength);
+        int numLength = moveNumberToken.token(moveNumber, pgn);
+        if (numLength > 0) {
+            pgn = pgn.substring(numLength);
         }
         Matcher matcher = MOVE_PATTERN.matcher(pgn);
         if (!matcher.find()) {
@@ -62,16 +61,8 @@ public class MoveToken implements Token<Moves> {
             moves.addMove(
                     detailMove
             );
-            String glyph = matcher.group(NAG_GROUP); // todo: more then one NAG
-            if (glyph != null) {
-                detailMove.addGlyph(
-                        Glyph.byNumber(
-                                Integer.parseInt(glyph)
-                        )
-                );
-            }
         }
-        return numberLength + matcher.group().length();
+        return numLength + matcher.group().length();
     }
 
 }
index 78de96c..fd0f21c 100644 (file)
@@ -32,10 +32,13 @@ public class MovesToken<Entity extends Moves> implements Token<Entity> {
 
     enum Type {
 
-        MOVE      ( new MoveToken()      ),
-        COMMENT   ( new CommentToken()   ),
-        VARIATION ( new VariationToken() ),
-        RESULT    ( new ResultToken()    );
+        MOVE      ( new MoveToken()                               ),
+        NAG       ( new NAGToken()                                ),
+        BRACE     ( new CommentToken(CommentToken.Type.BRACE)     ),
+        SEMICOLON ( new CommentToken(CommentToken.Type.SEMICOLON) ),
+        VARIATION ( new VariationToken()                          ),
+        RESULT    ( new ResultToken()                             ),
+        RESERVED  ( new ReservedToken()                           );
 
         private Token<Moves> token;
 
@@ -50,17 +53,21 @@ public class MovesToken<Entity extends Moves> implements Token<Entity> {
 
     private static final String MOVES_REGEX = "^\\s*([^\\s])";
     private static final Pattern MOVES_PATTERN = Pattern.compile(MOVES_REGEX);
+    private static final int TYPE_GROUP = 1;
 
     private static final String RESULT_REGEX = "^\\s*(1-0|0-1|1/2-1/2|\\*)";
     private static final Pattern RESULT_PATTERN = Pattern.compile(RESULT_REGEX);
 
-    private static final String COMMENT = "{";
+    private static final String NAG = "$";
+    private static final String BRACE = "{";
+    private static final String SEMICOLON = ";";
     private static final String VARIATION = "(";
+    private static final String RESERVED = "<";
 
     @Override
     public int token(Entity entity, String pgn) throws ParseException {
         int pgnLength = pgn.length();
-        pgn = PGNUtils.stripCrlf(pgn); // todo: starting with ";" comments
+        pgn = PGNUtils.tabCrlf(pgn);
         boolean isResulted = false;
         Type tokenType = tokenType(pgn);
         while (tokenType != null) {
@@ -82,13 +89,22 @@ public class MovesToken<Entity extends Moves> implements Token<Entity> {
         } else {
             Matcher moveMatcher = MOVES_PATTERN.matcher(pgn);
             if (moveMatcher.find()) {
-                switch (moveMatcher.group(1)) {
-                    case COMMENT:
-                        type = Type.COMMENT;
+                switch (moveMatcher.group(TYPE_GROUP)) {
+                    case NAG:
+                        type = Type.NAG;
+                        break;
+                    case BRACE:
+                        type = Type.BRACE;
+                        break;
+                    case SEMICOLON:
+                        type = Type.SEMICOLON;
                         break;
                     case VARIATION:
                         type = Type.VARIATION;
                         break;
+                    case RESERVED:
+                        type = Type.RESERVED;
+                        break;
                     default:
                         type = Type.MOVE;
                 }
diff --git a/chesshog-format/src/main/java/org/hedgecode/chess/pgn/token/NAGToken.java b/chesshog-format/src/main/java/org/hedgecode/chess/pgn/token/NAGToken.java
new file mode 100644 (file)
index 0000000..e598e01
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2018-2020. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.pgn.token;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.hedgecode.chess.ParseException;
+import org.hedgecode.chess.pgn.entity.Moves;
+import org.hedgecode.chess.pgn.nag.Glyph;
+
+/**
+ * NAGToken
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class NAGToken implements Token<Moves> {
+
+    private static final String NAG_REGEX = "^\\s*\\$([0-9]{1,3})";
+    private static final Pattern NAG_PATTERN = Pattern.compile(NAG_REGEX);
+    private static final int NAG_GROUP = 1;
+
+    @Override
+    public int token(Moves moves, String pgn) throws ParseException {
+        Matcher matcher = NAG_PATTERN.matcher(pgn);
+        if (!matcher.find()) {
+            throw new ParseException("parse.pgn.incorrect.nag", pgn);
+        } else {
+            Glyph glyph = Glyph.byNumber(
+                    Integer.parseInt(
+                            matcher.group(NAG_GROUP)
+                    )
+            );
+            if (glyph != null) {
+                moves.currentMove().addGlyph(glyph);
+            }
+        }
+        return matcher.group().length();
+    }
+
+}
diff --git a/chesshog-format/src/main/java/org/hedgecode/chess/pgn/token/ReservedToken.java b/chesshog-format/src/main/java/org/hedgecode/chess/pgn/token/ReservedToken.java
new file mode 100644 (file)
index 0000000..f36aa36
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2018-2020. Developed by Hedgecode.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.hedgecode.chess.pgn.token;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.hedgecode.chess.ParseException;
+import org.hedgecode.chess.pgn.entity.Moves;
+
+/**
+ * ReservedToken
+ *
+ * @author Dmitry Samoshin aka gotty
+ */
+public class ReservedToken implements Token<Moves> {
+
+    private static final String RESERVED_REGEX = "^\\s*<([^>]+)>";
+    private static final Pattern RESERVED_PATTERN = Pattern.compile(RESERVED_REGEX);
+
+    @Override
+    public int token(Moves moves, String pgn) throws ParseException {
+        Matcher matcher = RESERVED_PATTERN.matcher(pgn);
+        if (!matcher.find()) {
+            throw new ParseException("parse.pgn.incorrect.reserved", pgn);
+        }
+        return matcher.group().length();
+    }
+
+}
index 03550db..328db78 100644 (file)
@@ -26,12 +26,14 @@ parse.pgn.incorrect.tag=Incorrect PGN tag
 parse.pgn.missed.empty=Missed PGN empty line
 parse.pgn.incorrect.empty=Incorrect PGN empty line
 parse.pgn.incorrect.move=Incorrect PGN move
+parse.pgn.incorrect.nag=Incorrect NAG of PGN move
 parse.pgn.incorrect.comment=Incorrect PGN comment
 parse.pgn.incorrect.variation=Incorrect PGN variation
 parse.pgn.incorrect.result=Incorrect PGN result
 parse.pgn.symbols.after.result=Incorrect symbols after PGN result
 parse.pgn.result.not.match=Result in PGN tags and moves does not match
 parse.pgn.variation.result=Variations cannot contain PGN result
+parse.pgn.incorrect.reserved=Incorrect PGN reserved token
 
 parse.tcd.index.out.of.bounds=Array Index Out Of Bounds
 parse.tcd.invalid.bytes=Invalid input bytes
index 2b913d5..2ac5808 100644 (file)
@@ -25,12 +25,14 @@ parse.pgn.incorrect.tag=
 parse.pgn.missed.empty=
 parse.pgn.incorrect.empty=
 parse.pgn.incorrect.move=
+parse.pgn.incorrect.nag=
 parse.pgn.incorrect.comment=
 parse.pgn.incorrect.variation=
 parse.pgn.incorrect.result=
 parse.pgn.symbols.after.result=
 parse.pgn.result.not.match=
 parse.pgn.variation.result=
+parse.pgn.incorrect.reserved=
 
 parse.tcd.index.out.of.bounds=
 parse.tcd.invalid.bytes=