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.io.IOException; 024import java.io.Writer; 025import java.util.ArrayList; 026import java.util.Collection; 027import java.util.Collections; 028import java.util.Iterator; 029import java.util.LinkedHashMap; 030import java.util.List; 031import java.util.Map; 032 033import org.biojava.bio.seq.io.ParseException; 034 035/** 036 * Represents Nexus distances blocks. 037 * 038 * @author Richard Holland 039 * @author Tobias Thierer 040 * @author Jim Balhoff 041 * @since 1.6 042 */ 043public class DistancesBlock extends NexusBlock.Abstract { 044 045 /** 046 * A constant representing the name of Distances blocks. 047 */ 048 public static final String DISTANCES_BLOCK = "DISTANCES"; 049 050 private int dimensionsNTax = 0; 051 052 private int dimensionsNChar = 0; 053 054 private String triangle = "LOWER"; 055 056 private boolean diagonal = true; 057 058 private boolean labels = true; 059 060 private String missing = "?"; 061 062 private boolean interleaved = false; 063 064 private List taxLabels = new ArrayList(); 065 066 // values are lists, containing strings and nulls which are gaps 067 private Map matrix = new LinkedHashMap(); 068 069 private List comments = new ArrayList(); 070 071 /** 072 * Delegates to NexusBlock.Abstract constructor using 073 * DistancesBlock.DISTANCES_BLOCK as the name. 074 */ 075 public DistancesBlock() { 076 super(DistancesBlock.DISTANCES_BLOCK); 077 } 078 079 /** 080 * Set the NTAX value. 081 * 082 * @param dimensionsNTax 083 * the NTAX value. 084 */ 085 public void setDimensionsNTax(int dimensionsNTax) { 086 this.dimensionsNTax = dimensionsNTax; 087 } 088 089 /** 090 * Get the NTAX value. 091 * 092 * @return the NTAX value. 093 */ 094 public int getDimensionsNTax() { 095 return this.dimensionsNTax; 096 } 097 098 /** 099 * Set the NCHAR value. 100 * 101 * @param dimensionsNChar 102 * the NCHAR value. 103 */ 104 public void setDimensionsNChar(int dimensionsNChar) { 105 this.dimensionsNChar = dimensionsNChar; 106 } 107 108 /** 109 * Get the NCHAR value. 110 * 111 * @return the NCHAR value. 112 */ 113 public int getDimensionsNChar() { 114 return this.dimensionsNChar; 115 } 116 117 public void setTriangle(final String triangle) { 118 this.triangle = triangle; 119 } 120 121 public void setDiagonal(final boolean diagonal) { 122 this.diagonal = diagonal; 123 } 124 125 public boolean isDiagonal() { 126 return this.diagonal; 127 } 128 129 public void setLabels(final boolean labels) { 130 this.labels = labels; 131 } 132 133 public boolean isLabels() { 134 return this.labels; 135 } 136 137 public void setMissing(final String missing) { 138 this.missing = missing; 139 } 140 141 public String getMissing() { 142 return this.missing; 143 } 144 145 public void setInterleaved(final boolean interleaved) { 146 this.interleaved = interleaved; 147 } 148 149 public boolean isInterleaved() { 150 return this.interleaved; 151 } 152 153 /** 154 * Add a TAXLABEL. If it already exists, or is a number that refers to an 155 * index position that already exists, an exception is thrown. 156 * 157 * @param taxLabel 158 * the label to add. 159 * @throws ParseException 160 * if the label cannot be added. 161 */ 162 public void addTaxLabel(final String taxLabel) throws ParseException { 163 if (this.taxLabels.contains(taxLabel)) 164 throw new ParseException("Duplicate taxa label: " + taxLabel); 165 else 166 try { 167 // Try it as a number to see if it refers to 168 // position we already have. 169 final int i = Integer.parseInt(taxLabel); 170 if (i <= this.taxLabels.size() + 1) 171 throw new ParseException("Taxa label " + i 172 + " refers to already extant taxa position"); 173 } catch (NumberFormatException e) { 174 // It is not a number, so ignore. 175 } catch (ParseException e) { 176 // Throw it. 177 throw e; 178 } 179 this.taxLabels.add(taxLabel); 180 } 181 182 /** 183 * Removes the given TAXLABEL. 184 * 185 * @param taxLabel 186 * the label to remove. 187 */ 188 public void removeTaxLabel(final String taxLabel) { 189 this.taxLabels.remove(taxLabel); 190 } 191 192 /** 193 * Checks to see if we contain the given TAXLABEL. 194 * 195 * @param taxLabel 196 * the label to check for. 197 * @return <tt>true</tt> if we already contain it. 198 */ 199 public boolean containsTaxLabel(final String taxLabel) { 200 if (this.taxLabels.contains(taxLabel)) 201 return true; 202 else 203 try { 204 // Try it as a number to see if it refers to 205 // position we already have. 206 final int i = Integer.parseInt(taxLabel); 207 if (i <= this.taxLabels.size() + 1) 208 return true; 209 } catch (NumberFormatException e) { 210 // It is not a number, so ignore. 211 } 212 return false; 213 } 214 215 /** 216 * Get the TAXLABEL values added so far. 217 * 218 * @return this labels so far. 219 */ 220 public List getTaxLabels() { 221 return this.taxLabels; 222 } 223 224 public void addMatrixEntry(final String taxa) { 225 if (!this.matrix.containsKey(taxa)) 226 this.matrix.put(taxa, new ArrayList()); 227 } 228 229 public void appendMatrixData(final String taxa, final Object data) { 230 ((List) this.matrix.get(taxa)).add(data); 231 } 232 233 public List getMatrixData(final String taxa) { 234 return (List) this.matrix.get(taxa); 235 } 236 237 public Collection getMatrixLabels() { 238 return Collections.unmodifiableSet(this.matrix.keySet()); 239 } 240 241 /** 242 * Adds a comment. 243 * 244 * @param comment 245 * the comment to add. 246 */ 247 public void addComment(final NexusComment comment) { 248 this.comments.add(comment); 249 } 250 251 /** 252 * Removes a comment. 253 * 254 * @param comment 255 * the comment to remove. 256 */ 257 public void removeComment(final NexusComment comment) { 258 this.comments.remove(comment); 259 } 260 261 /** 262 * Returns all comments. 263 * 264 * @return all the selected comments. 265 */ 266 public List getComments() { 267 return this.comments; 268 } 269 270 protected void writeBlockContents(Writer writer) throws IOException { 271 for (final Iterator i = this.comments.iterator(); i.hasNext();) { 272 ((NexusComment) i.next()).writeObject(writer); 273 writer.write(NexusFileFormat.NEW_LINE); 274 } 275 writer.write(" DIMENSIONS "); 276 if (!this.taxLabels.isEmpty()) 277 writer.write("NEWTAXA "); 278 if (this.dimensionsNTax > 0) 279 writer.write("NTAX=" + this.dimensionsNTax + " "); 280 writer.write("NCHAR=" + this.dimensionsNChar + ";" 281 + NexusFileFormat.NEW_LINE); 282 283 writer.write(" FORMAT TRIANGLE="); 284 this.writeToken(writer, this.triangle); 285 writer.write(this.diagonal ? " DIAGONAL" : " NODIAGONAL"); 286 writer.write(this.labels ? " LABELS" : " NOLABELS"); 287 writer.write(" MISSING="); 288 this.writeToken(writer, this.missing); 289 if (this.interleaved) 290 writer.write(" INTERLEAVED"); 291 writer.write(";" + NexusFileFormat.NEW_LINE); 292 293 if (this.taxLabels.size() > 0) { 294 writer.write(" TAXLABELS"); 295 for (final Iterator i = this.taxLabels.iterator(); i.hasNext();) { 296 writer.write(' '); 297 this.writeToken(writer, (String) i.next()); 298 } 299 writer.write(";" + NexusFileFormat.NEW_LINE); 300 } 301 302 // if statesformat=statespresent and items=1, bracket only multi values, 303 // otherwise bracket all values 304 // only space tokens if reallyUseTokens=true 305 writer.write(" MATRIX" + NexusFileFormat.NEW_LINE); 306 for (final Iterator i = this.matrix.entrySet().iterator(); i.hasNext();) { 307 final Map.Entry entry = (Map.Entry) i.next(); 308 writer.write('\t'); 309 this.writeToken(writer, "" + entry.getKey()); 310 writer.write('\t'); 311 for (final Iterator j = ((List) entry.getValue()).iterator(); j 312 .hasNext();) { 313 final Object obj = j.next(); 314 if (obj instanceof String) 315 this.writeToken(writer, (String) obj); 316 if (j.hasNext()) 317 writer.write('\t'); 318 } 319 writer.write(NexusFileFormat.NEW_LINE); 320 } 321 writer.write(";" + NexusFileFormat.NEW_LINE); 322 323 } 324}