001package uk.ac.ebi.pride.jmztab2.utils.errors; 002 003import de.isas.mztab2.model.ValidationMessage; 004import java.io.Serializable; 005import java.util.ArrayList; 006import java.util.List; 007import java.util.regex.Matcher; 008import java.util.regex.Pattern; 009import static uk.ac.ebi.pride.jmztab2.model.MZTabConstants.NEW_LINE; 010 011/** 012 * mzTab files can be validated to ensure that they comply with the latest 013 * version of the format specification. The process includes two steps: first of 014 * all the basic model architecture is created, including the metadata section 015 * and the generation of the table column headers. The second step is the 016 * validation of the column rows, which take most of the processing time. The 017 * class MZTabFileParser is used to parse and validate the mzTab files. If the 018 * validation is successful, an MZTabFile model will be then generated. A series 019 * of messages are then reported, which can help to diagnose different types of 020 * format-related (reporting format problems) and/or logical (reporting errors 021 * related to the logical relationships among the different sections in a file) 022 * errors. At the moment of writing, there are about sixty types of error 023 * messages (http://mztab.googlecode.com/wiki/jmzTab_message). The validation 024 * messages have a unique identifier and are classified in three levels: Info, 025 * Warn and Error, according to the requirements included in the specification 026 * document. 027 * 028 * @author qingwei 029 * @since 06/02/13 030 * 031 */ 032public class MZTabError implements Serializable { 033 034 private int lineNumber; 035 private MZTabErrorType type; 036 private String message; 037 038 /** 039 * System will fill a couple of values one by one, and generate a concrete 040 * error message during parse {@link #lineNumber} line in mzTab file. 041 * 042 * @param type SHOULD NOT null. 043 * @param lineNumber SHOULD be positive integer. Except "-1", which means 044 * the line number unknown. 045 * @param values May be null, if no variable in error's original pattern. 046 */ 047 public MZTabError(MZTabErrorType type, int lineNumber, String... values) { 048 if (type == null) { 049 throw new NullPointerException("MZTabErrorType should not set null"); 050 } 051 this.type = type; 052 053 this.lineNumber = lineNumber; 054 055 List<String> valueList = new ArrayList<String>(); 056 for (String value : values) { 057 valueList.add(value == null ? "" : value); 058 } 059 060 this.message = fill(0, valueList, type.getOriginal()); 061 } 062 063 /** 064 * fill "{id}" parameter list one by one. 065 */ 066 private String fill(int count, List<String> values, String message) { 067 String regexp = "\\{\\w\\}"; 068 Pattern pattern = Pattern.compile(regexp); 069 Matcher matcher = pattern.matcher(message); 070 071 String value; 072 if (matcher.find()) { 073 if (count >= values.size()) { 074 throw new ArrayIndexOutOfBoundsException( 075 "Tried to replace placeholder " + (count + 1) + " but only " + values. 076 size() + " values are available for " + getClass(). 077 getSimpleName() + " " + type.toString()); 078 } 079 value = values.get(count); 080 message = matcher.replaceFirst(Matcher.quoteReplacement(value)); 081 return fill(count + 1, values, message); 082 } else { 083 return message; 084 } 085 } 086 087 /** 088 * <p>Getter for the field <code>type</code>.</p> 089 * 090 * @return {@link uk.ac.ebi.pride.jmztab2.utils.errors.MZTabErrorType} 091 * @see FormatErrorType 092 * @see LogicalErrorType 093 */ 094 public MZTabErrorType getType() { 095 return type; 096 } 097 098 /** 099 * <p>Getter for the field <code>message</code>.</p> 100 * 101 * @return a concrete error/warn message. 102 */ 103 public String getMessage() { 104 return message; 105 } 106 107 /** 108 * <p>Getter for the field <code>lineNumber</code>.</p> 109 * 110 * @return the line number. 111 */ 112 public int getLineNumber() { 113 return lineNumber; 114 } 115 116 /** 117 * {@inheritDoc} 118 * 119 * Code: Unique number for error/warn Category: Currently, there are three 120 * types of messages: Format, Logical Original: Message expression pattern. 121 * "{?}" is a couple of parameters which can be filled during validate 122 * processing. Cause: A readable text to describe the reason why raise this 123 * error/warn. Currently, these cause message coming from mztab 124 * specification mainly. 125 */ 126 @Override 127 public String toString() { 128 StringBuilder sb = new StringBuilder(); 129 130 sb.append("["). 131 append(type.getLevel()). 132 append("-"). 133 append(type.getCode()). 134 append("] "); 135 sb.append("line "). 136 append(lineNumber). 137 append(": "); 138 sb.append(message). 139 append(NEW_LINE); 140 141 return sb.toString(); 142 } 143 144 public ValidationMessage toValidationMessage() throws IllegalStateException { 145 ValidationMessage.MessageTypeEnum level = ValidationMessage.MessageTypeEnum.INFO; 146 switch (getType(). 147 getLevel()) { 148 case Error: 149 level = ValidationMessage.MessageTypeEnum.ERROR; 150 break; 151 case Info: 152 level = ValidationMessage.MessageTypeEnum.INFO; 153 break; 154 case Warn: 155 level = ValidationMessage.MessageTypeEnum.WARN; 156 break; 157 default: 158 throw new IllegalStateException("State " + 159 getType(). 160 getLevel() + " is not handled in switch/case statement!"); 161 } 162 ValidationMessage.CategoryEnum category = ValidationMessage.CategoryEnum.FORMAT; 163 switch(getType().getCategory()) { 164 case Format: 165 category = ValidationMessage.CategoryEnum.FORMAT; 166 break; 167 case Logical: 168 category = ValidationMessage.CategoryEnum.LOGICAL; 169 break; 170 case CrossCheck: 171 category = ValidationMessage.CategoryEnum.CROSS_CHECK; 172 break; 173 default: 174 throw new IllegalStateException("Category " + 175 getType(). 176 getCategory()+ " is not handled in switch/case statement!"); 177 } 178 ValidationMessage vr = new ValidationMessage().lineNumber( 179 Long.valueOf(getLineNumber())). 180 category(category). 181 messageType(level). 182 message(getMessage()). 183 code(toString()); 184 return vr; 185 } 186}