001/* 002 * BioJava development code 003 * 004 * This code may be freely distributed and modified under the 005 * terms of the GNU Lesser General Public Licence. This should 006 * be distributed with the code. If you do not have a copy, 007 * see: 008 * 009 * http://www.gnu.org/copyleft/lesser.html 010 * 011 * Copyright for this code is held jointly by the individual 012 * authors. These should be listed in @author doc comments. 013 * 014 * For more information on the BioJava project and its aims, 015 * or to join the biojava-l mailing list, visit the home page 016 * at: 017 * 018 * http://www.biojava.org/ 019 * 020 */ 021package org.biojavax.bio.phylo.io.nexus; 022 023import java.util.ArrayList; 024import java.util.List; 025 026import org.biojava.bio.seq.io.ParseException; 027 028/** 029 * Parses Nexus distances blocks. 030 * 031 * @author Richard Holland 032 * @author Tobias Thierer 033 * @author Jim Balhoff 034 * @since 1.6 035 */ 036public class DistancesBlockParser extends NexusBlockParser.Abstract { 037 038 private boolean expectingDimension; 039 040 private boolean expectingNewTaxa; 041 042 private boolean expectingNTax; 043 044 private boolean expectingNTaxEquals; 045 046 private boolean expectingNTaxValue; 047 048 private boolean expectingNChar; 049 050 private boolean expectingNCharEquals; 051 052 private boolean expectingNCharValue; 053 054 private boolean expectingFormat; 055 056 private boolean expectingTaxLabel; 057 058 private boolean expectingTaxLabelValue; 059 060 private boolean expectingMatrix; 061 062 private boolean expectingTriangle; 063 064 private boolean expectingTriangleEquals; 065 066 private boolean expectingTriangleContent; 067 068 private boolean expectingDiagonal; 069 070 private boolean expectingMissing; 071 072 private boolean expectingMissingEquals; 073 074 private boolean expectingMissingContent; 075 076 private boolean expectingLabels; 077 078 private boolean expectingInterleave; 079 080 private boolean expectingMatrixKey; 081 082 private boolean expectingMatrixContent; 083 084 private String currentMatrixKey; 085 086 private String matrixFirstLineKey; 087 088 private List matrixSeenKeys = new ArrayList(); 089 090 private String triangleType; 091 092 /** 093 * Delegates to NexusBlockParser.Abstract. 094 * 095 * @param blockListener 096 * the listener to send parse events to. 097 */ 098 public DistancesBlockParser(DistancesBlockListener blockListener) { 099 super(blockListener); 100 } 101 102 public void resetStatus() { 103 this.expectingDimension = true; 104 this.expectingNewTaxa = false; 105 this.expectingNTax = false; 106 this.expectingNTaxEquals = false; 107 this.expectingNTaxValue = false; 108 this.expectingNChar = false; 109 this.expectingNCharEquals = false; 110 this.expectingNCharValue = false; 111 this.expectingFormat = true; 112 this.expectingDiagonal = false; 113 this.expectingTaxLabel = true; 114 this.expectingTaxLabelValue = false; 115 this.expectingMatrix = true; 116 this.expectingTriangle = false; 117 this.expectingTriangleEquals = false; 118 this.expectingTriangleContent = false; 119 this.expectingMissing = false; 120 this.expectingMissingEquals = false; 121 this.expectingMissingContent = false; 122 this.expectingLabels = false; 123 this.expectingInterleave = false; 124 this.expectingMatrixKey = false; 125 this.expectingMatrixContent = false; 126 this.currentMatrixKey = null; 127 this.matrixFirstLineKey = null; 128 this.triangleType = "LOWER"; 129 this.matrixSeenKeys.clear(); 130 } 131 132 public boolean wantsBracketsAndBraces() { 133 return this.expectingMatrixContent; 134 } 135 136 public void parseToken(String token) throws ParseException { 137 if (this.expectingMatrixContent 138 && "\n".equals(token)) { 139 // Special handling for new lines inside matrix data. 140 this.expectingMatrixContent = false; 141 this.expectingMatrixKey = true; 142 } else if (token.trim().length() == 0) 143 return; 144 else if (this.expectingDimension 145 && "DIMENSIONS".equalsIgnoreCase(token)) { 146 this.expectingDimension = false; 147 this.expectingNewTaxa = true; 148 this.expectingNChar = true; 149 } else if (this.expectingNewTaxa && "NEWTAXA".equalsIgnoreCase(token)) { 150 this.expectingNewTaxa = false; 151 this.expectingNTax = true; 152 this.expectingNChar = false; 153 } else if (this.expectingNTax && token.toUpperCase().startsWith("NTAX")) { 154 this.expectingNTax = false; 155 if (token.indexOf('=') >= 0) { 156 final String[] parts = token.split("="); 157 if (parts.length > 1) { 158 this.expectingNChar = true; 159 try { 160 ((DistancesBlockListener) this.getBlockListener()) 161 .setDimensionsNTax(Integer.parseInt(parts[1])); 162 } catch (NumberFormatException e) { 163 throw new ParseException("Invalid NTAX value: " 164 + parts[1]); 165 } 166 } else 167 this.expectingNTaxValue = true; 168 } else 169 this.expectingNTaxEquals = true; 170 } else if (this.expectingNTaxEquals && token.startsWith("=")) { 171 this.expectingNTaxEquals = false; 172 final String[] parts = token.split("="); 173 if (parts.length > 1) { 174 this.expectingNChar = true; 175 try { 176 ((DistancesBlockListener) this.getBlockListener()) 177 .setDimensionsNTax(Integer.parseInt(parts[1])); 178 } catch (NumberFormatException e) { 179 throw new ParseException("Invalid NTAX value: " + parts[1]); 180 } 181 } else 182 this.expectingNTaxValue = true; 183 } else if (this.expectingNTaxValue) { 184 this.expectingNTaxValue = false; 185 try { 186 ((DistancesBlockListener) this.getBlockListener()) 187 .setDimensionsNTax(Integer.parseInt(token)); 188 } catch (NumberFormatException e) { 189 throw new ParseException("Invalid NTAX value: " + token); 190 } 191 this.expectingNChar = true; 192 } else if (this.expectingNChar 193 && token.toUpperCase().startsWith("NCHAR")) { 194 this.expectingNChar = false; 195 if (token.indexOf('=') >= 0) { 196 final String[] parts = token.split("="); 197 if (parts.length > 1) { 198 try { 199 ((DistancesBlockListener) this.getBlockListener()) 200 .setDimensionsNChar(Integer.parseInt(parts[1])); 201 } catch (NumberFormatException e) { 202 throw new ParseException("Invalid NCHAR value: " 203 + parts[1]); 204 } 205 } else 206 this.expectingNCharValue = true; 207 } else 208 this.expectingNCharEquals = true; 209 } else if (this.expectingNCharEquals && token.startsWith("=")) { 210 this.expectingNCharEquals = false; 211 final String[] parts = token.split("="); 212 if (parts.length > 1) { 213 try { 214 ((DistancesBlockListener) this.getBlockListener()) 215 .setDimensionsNChar(Integer.parseInt(parts[1])); 216 } catch (NumberFormatException e) { 217 throw new ParseException("Invalid NCHAR value: " + parts[1]); 218 } 219 } else 220 this.expectingNCharValue = true; 221 } else if (this.expectingNCharValue) { 222 this.expectingNCharValue = false; 223 try { 224 ((DistancesBlockListener) this.getBlockListener()) 225 .setDimensionsNChar(Integer.parseInt(token)); 226 } catch (NumberFormatException e) { 227 throw new ParseException("Invalid NCHAR value: " + token); 228 } 229 } 230 231 else if (this.expectingFormat && "FORMAT".equalsIgnoreCase(token)) { 232 this.expectingFormat = false; 233 this.expectingTriangle = true; 234 this.expectingDiagonal = true; 235 this.expectingMissing = true; 236 this.expectingLabels = true; 237 this.expectingInterleave = true; 238 } 239 240 else if (this.expectingTriangle 241 && token.toUpperCase().startsWith("TRIANGLE")) { 242 this.expectingTriangle = false; 243 244 if (token.indexOf("=") >= 0) { 245 final String[] parts = token.split("="); 246 if (parts.length > 1) { 247 this.triangleType = parts[1]; 248 ((DistancesBlockListener) this.getBlockListener()) 249 .setTriangle(parts[1]); 250 } else 251 this.expectingTriangleContent = true; 252 } else 253 this.expectingTriangleEquals = true; 254 } 255 256 else if (this.expectingTriangleEquals && token.startsWith("=")) { 257 this.expectingTriangleEquals = false; 258 if (token.length() > 1) { 259 token = token.substring(1); 260 this.triangleType = token; 261 ((DistancesBlockListener) this.getBlockListener()) 262 .setTriangle(token); 263 } else 264 this.expectingTriangleContent = true; 265 } 266 267 else if (this.expectingTriangleContent) { 268 this.triangleType = token; 269 ((DistancesBlockListener) this.getBlockListener()) 270 .setTriangle(token); 271 this.expectingTriangleContent = false; 272 } 273 274 else if (this.expectingDiagonal && "DIAGONAL".equalsIgnoreCase(token)) { 275 ((DistancesBlockListener) this.getBlockListener()) 276 .setDiagonal(true); 277 this.expectingDiagonal = false; 278 } 279 280 else if (this.expectingDiagonal && "NODIAGONAL".equalsIgnoreCase(token)) { 281 ((DistancesBlockListener) this.getBlockListener()) 282 .setDiagonal(false); 283 this.expectingDiagonal = false; 284 } 285 286 else if (this.expectingLabels && "LABELS".equalsIgnoreCase(token)) { 287 ((DistancesBlockListener) this.getBlockListener()).setLabels(true); 288 this.expectingLabels = false; 289 } 290 291 else if (this.expectingLabels && "NOLABELS".equalsIgnoreCase(token)) { 292 ((DistancesBlockListener) this.getBlockListener()).setLabels(false); 293 this.expectingLabels = false; 294 } 295 296 else if (this.expectingMissing 297 && token.toUpperCase().startsWith("MISSING")) { 298 this.expectingMissing = false; 299 300 if (token.indexOf("=") >= 0) { 301 final String[] parts = token.split("="); 302 if (parts.length > 1) 303 ((DistancesBlockListener) this.getBlockListener()) 304 .setMissing(parts[1]); 305 else 306 this.expectingMissingContent = true; 307 } else 308 this.expectingMissingEquals = true; 309 } 310 311 else if (this.expectingMissingEquals && token.startsWith("=")) { 312 this.expectingMissingEquals = false; 313 if (token.length() > 1) 314 ((DistancesBlockListener) this.getBlockListener()) 315 .setMissing(token.substring(1)); 316 else 317 this.expectingMissingContent = true; 318 } 319 320 else if (this.expectingMissingContent) { 321 ((DistancesBlockListener) this.getBlockListener()) 322 .setMissing(token); 323 this.expectingMissingContent = false; 324 } 325 326 else if (this.expectingInterleave 327 && "INTERLEAVE".equalsIgnoreCase(token)) { 328 ((DistancesBlockListener) this.getBlockListener()) 329 .setInterleaved(true); 330 this.expectingInterleave = false; 331 } 332 333 else if (this.expectingTaxLabel && "TAXLABELS".equalsIgnoreCase(token)) { 334 this.expectingFormat = false; 335 this.expectingTriangle = false; 336 this.expectingLabels = false; 337 this.expectingDiagonal = false; 338 this.expectingMissing = false; 339 this.expectingInterleave = false; 340 this.expectingTaxLabel = false; 341 this.expectingTaxLabelValue = true; 342 } 343 344 else if (this.expectingMatrix && "MATRIX".equalsIgnoreCase(token)) { 345 this.expectingFormat = false; 346 this.expectingTriangle = false; 347 this.expectingLabels = false; 348 this.expectingDiagonal = false; 349 this.expectingMissing = false; 350 this.expectingInterleave = false; 351 this.expectingTaxLabel = false; 352 this.expectingTaxLabelValue = false; 353 this.expectingMatrix = false; 354 this.expectingMatrixKey = true; 355 } 356 357 else if (this.expectingTaxLabelValue) 358 // Use untoken version to preserve spaces. 359 ((DistancesBlockListener) this.getBlockListener()) 360 .addTaxLabel(token); 361 362 else if (this.expectingMatrixKey) { 363 this.currentMatrixKey = token; 364 // Use untoken version to preserve spaces. 365 ((DistancesBlockListener) this.getBlockListener()) 366 .addMatrixEntry(token); 367 this.expectingMatrixKey = false; 368 this.expectingMatrixContent = true; 369 // Update first line info and set up stack for entry. 370 if (!this.matrixSeenKeys.contains(token)) { 371 if (this.triangleType.equalsIgnoreCase("UPPER")) 372 for (int i = 0; i < this.matrixSeenKeys.size(); i++) 373 ((DistancesBlockListener) this.getBlockListener()) 374 .appendMatrixData(this.currentMatrixKey, null); 375 this.matrixSeenKeys.add(token); 376 } 377 if (this.matrixFirstLineKey == null) 378 this.matrixFirstLineKey = this.currentMatrixKey; 379 } 380 381 else if (this.expectingMatrixContent) 382 ((DistancesBlockListener) this.getBlockListener()) 383 .appendMatrixData(this.currentMatrixKey, token); 384 385 else 386 throw new ParseException("Found unexpected token " + token 387 + " in DISTANCES block"); 388 } 389}