001/* 002 * Copyright 2018 Leibniz-Institut für Analytische Wissenschaften – ISAS – e.V.. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package uk.ac.ebi.pride.jmztab2.model; 017 018import de.isas.mztab2.io.serialization.Serializers; 019import de.isas.mztab2.model.IndexedElement; 020 021/** 022 * Define a column header which used in {@link uk.ac.ebi.pride.jmztab2.model.Section#Protein_Header}, {@link uk.ac.ebi.pride.jmztab2.model.Section#Peptide_Header}, 023 * {@link uk.ac.ebi.pride.jmztab2.model.Section#PSM_Header}, or {@link uk.ac.ebi.pride.jmztab2.model.Section#Small_Molecule_Header}. There are two kinds of columns: stable column 024 * and optional column. Stable column has stable position and header name, while optional column not. 025 * {@link uk.ac.ebi.pride.jmztab2.model.MZTabColumnFactory} used to create and maintain these column objects. 026 * 027 * @see MZTabColumnFactory 028 * @see SmallMoleculeColumn 029 * @see SmallMoleculeFeatureColumn 030 * @see SmallMoleculeEvidenceColumn 031 * @see OptionColumn 032 * @see ParameterOptionColumn 033 * @see AbundanceColumn 034 * @author qingwei 035 * @author nilshoffmann 036 * @since 23/05/13 037 * 038 */ 039public class MZTabColumn implements IMZTabColumn { 040 private final String name; 041 private String order; 042 private Integer id; 043 private String header; 044 private String logicPosition; 045 private Class dataType; 046 private boolean optional; 047 048 private IndexedElement element; 049 050 /** 051 * Create a column header object. Default, the column header keep the same value with name, and logical position keep 052 * the same value with order. 053 * 054 * @param name define a stable name for column. For optional column, only set stable part for name. 055 * @param dataType define the data type for column. 056 * @param optional if false the column is stable type, otherwise is optional column. 057 * @param order internal order. Every non {@link uk.ac.ebi.pride.jmztab2.model.OptionColumn} has stable order. Column order is used to maintain the 058 * logical position in {@link uk.ac.ebi.pride.jmztab2.model.MZTabColumnFactory} 059 */ 060 public MZTabColumn(String name, Class dataType, boolean optional, String order) { 061 this(name, dataType, optional, order, null); 062 } 063 064 /** 065 * Create a column header object. Default, the column header keep the same value with name, and logical position keep 066 * the same value with order. 067 * 068 * @param name define a stable name for column. For optional column, only set stable part for name. 069 * @param dataType define the data type for column. 070 * @param optional if false the column is stable type, otherwise is optional column. 071 * @param order internal order. Every non {@link uk.ac.ebi.pride.jmztab2.model.OptionColumn} has stable order. Column order is used to maintain the 072 * logical position in {@link uk.ac.ebi.pride.jmztab2.model.MZTabColumnFactory} 073 * @param id incremental index used for some optional columns like best_search_engine_score[1], best_search_engine_score[2] 074 */ 075 public MZTabColumn(String name, Class dataType, boolean optional, String order, Integer id) { 076 if (MZTabStringUtils.isEmpty(name)) { 077 throw new IllegalArgumentException("Column name should not empty."); 078 } 079 this.name = name; 080 081 if (dataType == null) { 082 throw new NullPointerException("Column data type should not set null!"); 083 } 084 this.dataType = dataType; 085 this.optional = optional; 086 this.order = order; 087 this.id = id; 088 089 this.header = generateHeader(name); 090 this.logicPosition = generateLogicalPosition(); 091 } 092 093 private String generateHeader(String name) { 094 StringBuilder sb = new StringBuilder(); 095 096 sb.append(name); 097 if (id != null) { 098 sb.append("[").append(id).append("]"); 099 } 100 101 return sb.toString(); 102 } 103 104 private String generateLogicalPosition() { 105 StringBuilder sb = new StringBuilder(); 106 107 sb.append(order); 108 if (id != null) { 109 // generate id string which length is 2. Eg. 12, return 12; 1, return 01 110 sb.append(String.format("%02d", id)); 111 } else { 112 sb.append("00"); 113 } 114 115 if (element != null) { 116 sb.append(String.format("%02d", element.getId())); 117 } else { 118 sb.append("00"); 119 } 120 121 return sb.toString(); 122 } 123 124 125 /** 126 * {@inheritDoc} 127 * 128 * Get the column name. For stable column, name and header are same. But for optional column, name is part 129 * of its header. For example, optional column which header is search_engine_score_ms_run[1-n], and its name 130 * is search_engine_score. Besides this, ms_run[1-n] is kind of {@link #element} 131 * 132 * Notice: this design pattern not fit for {@link AbundanceColumn}, {@link OptionColumn} and {@link ParameterOptionColumn}. 133 * These optional columns need be generated by calling {@link MZTabColumnFactory} 's methods. 134 * @see #getHeader() 135 * @see #setElement(IndexedElement) 136 * @see #getHeader() 137 * @see #setElement(IndexedElement) 138 */ 139 @Override 140 public String getName() { 141 return name; 142 } 143 144 /** 145 * {@inheritDoc} 146 * 147 * Get the column internal order. For stable column, order and logical position are same. But for optional column, 148 * the logical position need to be calculated by concatenating order and index element id. For example, optional column 149 * search_engine_score_ms_run[2] in Protein section, its order is 09, and the logical position is 092. Because the 150 * element ms_run[2] 's index is 2. 151 * 152 * Notice: this design pattern not fit for {@link AbundanceColumn}, {@link OptionColumn} and {@link ParameterOptionColumn}. 153 * These optional columns need be generated by calling {@link MZTabColumnFactory} 's methods. 154 * @see #getLogicPosition() 155 */ 156 @Override 157 public String getOrder() { 158 return order; 159 } 160 161 /* 162 * Allows to reassign the order in case the file doesn't follow the recommended order 163 */ 164 /** {@inheritDoc} */ 165 @Override 166 public void setOrder(String order) { 167 //As the order change, the logic position need to be regenerated. 168 this.order = order; 169 this.logicPosition = generateLogicalPosition(); 170 171 } 172 173 /** 174 * {@inheritDoc} 175 * 176 * Get the column name. For stable column, name and header are same. While for optional column, name is part 177 * of its header. For example, optional column which header is search_engine_score_ms_run[1-n], and its name 178 * is search_engine_score. Besides this, ms_run[1-n] is kind of {@link #element} 179 * 180 * Notice: this design pattern not fit for {@link AbundanceColumn}, {@link OptionColumn} and {@link ParameterOptionColumn}. 181 * These optional columns need be generated by calling {@link MZTabColumnFactory} 's methods. 182 * @see #getName() 183 * @see #setElement(IndexedElement) 184 * @see #getName() 185 * @see #setElement(IndexedElement) 186 */ 187 @Override 188 public String getHeader() { 189 return header; 190 } 191 192 /** {@inheritDoc} */ 193 @Override 194 public void setHeader(String header) { 195 this.header = header; 196 } 197 198 /** 199 * {@inheritDoc} 200 * 201 * Get the column logical position. For stable column, order and logical position are same. But for optional column, 202 * the logical position need to calculate by concatenate order and index element id. For example, optional column 203 * search_engine_score_ms_run[2] in Protein section, its order is 09, and the logical position is 092. Because the 204 * element ms_run[2] 's index is 2. 205 * 206 *<p>Notice: this design pattern not fit for {@link AbundanceColumn}, {@link OptionColumn} and {@link ParameterOptionColumn}. 207 * These optional columns need be generated by calling {@link MZTabColumnFactory} 's methods.</p> 208 * 209 *<p>Notice: in {@link MZTabColumnFactory}, we use logical position to maintain the logical consistence within the {@link de.isas.mztab2.model.MzTab} file. 210 * During the process of parsing mzTab file, we create a mapping between physical position and internal logical position.</p> 211 * @see #getOrder() 212 */ 213 @Override 214 public String getLogicPosition() { 215 generateLogicalPosition(); 216 return logicPosition; 217 } 218 219 /** {@inheritDoc} */ 220 @Override 221 public void setLogicPosition(String logicPosition) { 222 this.logicPosition = logicPosition; 223 } 224 225 /** 226 * {@inheritDoc} 227 * 228 * Get the column data type Class. 229 */ 230 @Override 231 public Class getDataType() { 232 return dataType; 233 } 234 235 /** 236 * {@inheritDoc} 237 * 238 * Judge this column belong to stable column or optional column. 239 */ 240 @Override 241 public boolean isOptional() { 242 return optional; 243 } 244 245 /** 246 * {@inheritDoc} 247 * 248 * Indexed element used in optional column header and logical position definition. 249 * In stable column, the return is null. 250 * 251 * Notice: this design pattern not fit for {@link AbundanceColumn}, {@link OptionColumn} and {@link ParameterOptionColumn}. 252 * These optional columns need be generated by calling {@link MZTabColumnFactory} 's methods. 253 * @see #getHeader() 254 * @see #getLogicPosition() 255 * @see #getHeader() 256 * @see #getLogicPosition() 257 */ 258 @Override 259 public IndexedElement getElement() { 260 return element; 261 } 262 263 /** 264 * {@inheritDoc} 265 * 266 * Indexed element used in optional column header and logical position definition. 267 * In stable column, the return is null. 268 * 269 * Notice: this design pattern not fit for {@link AbundanceColumn}, {@link OptionColumn} and {@link ParameterOptionColumn}. 270 * These optional columns need be generated by calling {@link MZTabColumnFactory} 's methods. 271 * @see #getHeader() 272 * @see #getLogicPosition() 273 * @see #getHeader() 274 * @see #getLogicPosition() 275 */ 276 public void setElement(IndexedElement element) { 277 if (element == null) { 278 throw new NullPointerException("Can not set null indexed element for optional column!"); 279 } 280 this.element = element; 281 282 this.logicPosition = generateLogicalPosition(); 283 StringBuilder sb = new StringBuilder(); 284 if(this instanceof AbundanceColumn) { 285 sb.append(this.header).append("["). 286 append(element.getId()). 287 append("]"); 288 } else { 289 sb.append(this.header).append("_").append(Serializers.getReference(element, element.getId())); 290 } 291 this.header = sb.toString(); 292 } 293 294 /** 295 * Create a optional column for {@link Section#Protein_Header}, {@link Section#Peptide_Header}, 296 * {@link Section#PSM_Header}, {@link Section#Small_Molecule_Header}, {@link Section#Small_Molecule_Feature_Header}, 297 * or {@link Section#Small_Molecule_Evidence_Header}. 298 * 299 * Notice: this function only used to create stable order optional column, such as num_psms_ms_run[1-n] and so on. 300 * Not used to create {@link AbundanceColumn}, {@link OptionColumn} and {@link ParameterOptionColumn}. 301 * These optional columns need be generated by calling {@link MZTabColumnFactory} 's methods. 302 * 303 * @see MZTabColumnFactory#addOptionalColumn(MZTabColumn, MsRun) 304 */ 305 static IMZTabColumn createOptionalColumn(Section section, IMZTabColumn column, Integer id, IndexedElement element) { 306 if (! column.isOptional()) { 307 throw new IllegalArgumentException(column + " is not optional column!"); 308 } 309 310 IMZTabColumn optionColumn = null; 311 switch (section) { 312// case Protein_Header: 313// optionColumn = new ProteinColumn(column.getName(), column.getDataType(), column.isOptional(), column.getOrder(), id); 314// break; 315// case Peptide_Header: 316// optionColumn = new PeptideColumn(column.getName(), column.getDataType(), column.isOptional(), column.getOrder(), id); 317// break; 318// case PSM_Header: 319// optionColumn = new PSMColumn(column.getName(), column.getDataType(), column.isOptional(), column.getOrder(), id); 320// break; 321 case Small_Molecule_Header: 322 optionColumn = new SmallMoleculeColumn(column.getName(), column.getDataType(), column.isOptional(), column.getOrder(), id); 323 break; 324 case Small_Molecule_Feature_Header: 325 optionColumn = new SmallMoleculeFeatureColumn(column.getName(), column.getDataType(), column.isOptional(), column.getOrder(), id); 326 break; 327 case Small_Molecule_Evidence_Header: 328 optionColumn = new SmallMoleculeEvidenceColumn(column.getName(), column.getDataType(), column.isOptional(), column.getOrder(), id); 329 break; 330 } 331 332 if (optionColumn != null && element != null) { 333 optionColumn.setElement(element); 334 } 335 336 return optionColumn; 337 } 338 339 /** {@inheritDoc} */ 340 @Override 341 public String toString() { 342 return "MZTabColumn{" + 343 "header='" + header + '\'' + 344 ", logicPosition='" + logicPosition + '\'' + 345 ", dataType=" + dataType + 346 ", optional=" + optional + 347 '}'; 348 } 349 350 /** {@inheritDoc} */ 351 @Override 352 public boolean equals(Object o) { 353 if (this == o) return true; 354 if (o == null || getClass() != o.getClass()) return false; 355 356 MZTabColumn column = (MZTabColumn) o; 357 358 if (optional != column.optional) return false; 359 if (dataType != null ? !dataType.equals(column.dataType) : column.dataType != null) return false; 360 return (header != null ? header.equals(column.header) : column.header == null) && (logicPosition != null ? logicPosition.equals(column.logicPosition) : column.logicPosition == null); 361 } 362 363 /** {@inheritDoc} */ 364 @Override 365 public int hashCode() { 366 int result = header != null ? header.hashCode() : 0; 367 result = 31 * result + (logicPosition != null ? logicPosition.hashCode() : 0); 368 result = 31 * result + (dataType != null ? dataType.hashCode() : 0); 369 result = 31 * result + (optional ? 1 : 0); 370 return result; 371 } 372}