1 // SgfUtil.java 2 3 package net.sf.gogui.sgf; 4 5 import java.util.regex.Matcher; 6 import java.util.regex.Pattern; 7 import net.sf.gogui.game.TimeSettings; 8 9 public final class SgfUtil 10 { 11 /** Result of parseOvertime(). */ 12 public static final class Overtime 13 { 14 public long m_byoyomi; 15 16 public int m_byoyomiMoves; 17 } 18 19 /** Format byoyomi information for OT property. 20 The format is "N moves / S min" or "N moves / S sec" 21 This format is also recognized by parseOvertime. 22 Returns null, if timeSettings does not define byoyomi */ getOvertime(TimeSettings timeSettings)23 public static String getOvertime(TimeSettings timeSettings) 24 { 25 if (! timeSettings.getUseByoyomi()) 26 return null; 27 StringBuilder result = new StringBuilder(); 28 int byoyomiMoves = timeSettings.getByoyomiMoves(); 29 long byoyomi = timeSettings.getByoyomi(); 30 result.append(byoyomiMoves); 31 result.append(" moves / "); 32 if (byoyomi % 60000 == 0) 33 { 34 result.append(byoyomi / 60000L); 35 result.append(" min"); 36 } 37 else 38 { 39 result.append(byoyomi / 1000L); 40 result.append(" sec"); 41 } 42 return result.toString(); 43 } 44 parseOvertime(String value)45 public static Overtime parseOvertime(String value) 46 { 47 value = value.trim(); 48 Overtime result = null; 49 50 /* Used by SgfWriter */ 51 result = 52 parseOvertime(value, "(\\d+)\\s*moves\\s*/\\s*(\\d+)\\s*sec", 53 true, 1000L); 54 if (result != null) 55 return result; 56 57 /* Used by Smart Go */ 58 result = 59 parseOvertime(value, "(\\d+)\\s*moves\\s*/\\s*(\\d+)\\s*min", 60 true, 60000L); 61 if (result != null) 62 return result; 63 64 /* Used by Quarry, CGoban 2 */ 65 result = 66 parseOvertime(value, "(\\d+)/(\\d+)\\s*canadian", true, 1000L); 67 if (result != null) 68 return result; 69 70 return result; 71 } 72 73 /** Parse value of TM property. 74 According to FF4, TM needs to be a real value, but older SGF versions 75 allow a string with unspecified content. We try to parse a few known 76 formats. 77 @return The (pre-byoyomi-)time in milliseconds or -1, if the 78 format was not recognized */ parseTime(String value)79 public static long parseTime(String value) 80 { 81 value = value.trim(); 82 try 83 { 84 return (long)(Double.parseDouble(value) * 1000); 85 } 86 catch (NumberFormatException e1) 87 { 88 } 89 try 90 { 91 Pattern pattern; 92 Matcher matcher; 93 94 // Pattern as written by CGoban 1.9.12 95 pattern = Pattern.compile("(\\d{1,2}):(\\d{2})"); 96 matcher = pattern.matcher(value); 97 if (matcher.matches()) 98 { 99 assert matcher.groupCount() == 2; 100 return (Integer.parseInt(matcher.group(1)) * 60000L 101 + Integer.parseInt(matcher.group(2)) * 1000L); 102 } 103 104 pattern = Pattern.compile("(\\d+):(\\d{2}):(\\d{2})"); 105 matcher = pattern.matcher(value); 106 if (matcher.matches()) 107 { 108 assert matcher.groupCount() == 3; 109 return (Integer.parseInt(matcher.group(1)) * 3600000L 110 + Integer.parseInt(matcher.group(2)) * 60000L 111 + Integer.parseInt(matcher.group(3)) * 1000L); 112 } 113 114 pattern = 115 Pattern.compile("(\\d+)\\s*(?:h|hr|hrs|hours|hours)(?:\\s+each)?+"); 116 matcher = pattern.matcher(value); 117 if (matcher.matches()) 118 { 119 assert matcher.groupCount() == 1; 120 return Integer.parseInt(matcher.group(1)) * 3600000L; 121 } 122 123 pattern = 124 Pattern.compile("(\\d+)\\s*(?:m|min)"); 125 matcher = pattern.matcher(value); 126 if (matcher.matches()) 127 { 128 assert matcher.groupCount() == 1; 129 return Integer.parseInt(matcher.group(1)) * 60000L; 130 } 131 } 132 catch (NumberFormatException e2) 133 { 134 assert false; // patterns should match only valid integers 135 } 136 return -1; 137 } 138 139 /** Make constructor unavailable; class is for namespace only. */ SgfUtil()140 private SgfUtil() 141 { 142 } 143 parseOvertime(String value, String regex, boolean byoyomiMovesFirst, long timeUnitFactor)144 private static Overtime parseOvertime(String value, String regex, 145 boolean byoyomiMovesFirst, 146 long timeUnitFactor) 147 { 148 Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE); 149 Matcher matcher = pattern.matcher(value); 150 if (matcher.matches()) 151 { 152 assert matcher.groupCount() == 2; 153 try 154 { 155 String group1; 156 String group2; 157 if (byoyomiMovesFirst) 158 { 159 group1 = matcher.group(1); 160 group2 = matcher.group(2); 161 } 162 else 163 { 164 group1 = matcher.group(2); 165 group2 = matcher.group(1); 166 } 167 Overtime overtime = new Overtime(); 168 overtime.m_byoyomiMoves = Integer.parseInt(group1); 169 overtime.m_byoyomi = 170 (long)(Double.parseDouble(group2) * timeUnitFactor); 171 return overtime; 172 } 173 catch (NumberFormatException e) 174 { 175 // should not happen if patterns match only integer 176 assert false; 177 return null; 178 } 179 } 180 else 181 return null; 182 } 183 } 184