001/* 002 * Copyright 2020 nils.hoffmann. 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 de.isas.lipidomics.generator; 017 018import com.opencsv.CSVReader; 019import com.squareup.javapoet.AnnotationSpec; 020import com.squareup.javapoet.ClassName; 021import com.squareup.javapoet.CodeBlock; 022import com.squareup.javapoet.JavaFile; 023import com.squareup.javapoet.MethodSpec; 024import com.squareup.javapoet.ParameterizedTypeName; 025import com.squareup.javapoet.TypeName; 026import com.squareup.javapoet.TypeSpec; 027import com.squareup.javapoet.TypeSpec.Builder; 028import de.isas.lipidomics.domain.ElementTable; 029import de.isas.lipidomics.domain.LipidCategory; 030import de.isas.lipidomics.domain.LipidClass; 031import java.io.IOException; 032import java.io.Reader; 033import java.net.URISyntaxException; 034import java.nio.file.Files; 035import java.nio.file.Paths; 036import java.util.ArrayList; 037import java.util.Arrays; 038import java.util.Iterator; 039import java.util.List; 040import java.util.Optional; 041import java.util.logging.Level; 042import java.util.logging.Logger; 043import java.util.stream.Collectors; 044import java.util.stream.Stream; 045import javax.lang.model.element.Modifier; 046import lombok.Value; 047import lombok.extern.slf4j.Slf4j; 048 049/** 050 * 051 * @author nils.hoffmann 052 */ 053public class LipidClassGenerator { 054 055 public static void main(String... args) { 056 LipidClassGenerator gne = new LipidClassGenerator(); 057 try { 058 System.out.println(gne.getEnumFromTable(gne.getEnumEntries())); 059 } catch (IOException ex) { 060 Logger.getLogger(LipidClassGenerator.class.getName()).log(Level.SEVERE, null, ex); 061 } 062 } 063 064 @Value 065 public static class LipidClassEntry { 066 067 private final String lipidName; 068 private final String lipidCategory; 069 private final String lipidDescription; 070 private final Integer maxNumFa; 071 private final String allowedNumFa; 072 private final String sumFormula; 073 private final List<String> synonyms; 074 } 075 076 public Stream<LipidClassEntry> getEnumEntries() throws IOException { 077 try (Reader reader = Files.newBufferedReader(Paths.get(ClassLoader.getSystemResource("lipid-list.csv").toURI()))) {; 078 CSVReader csvReader = new CSVReader(reader); 079 csvReader.skip(1); 080 Iterator<String[]> iter = csvReader.iterator(); 081 List<LipidClassEntry> entries = new ArrayList<>(); 082 while (iter.hasNext()) { 083 String[] s = iter.next(); 084 int len = s.length; 085 List<String> synonyms = new ArrayList<>(); 086 for (int i = 6; i < len; i++) { 087 if (!s[i].trim().isEmpty()) { 088 synonyms.add(s[i]); 089 } 090 } 091 LipidClassEntry entry = new LipidClassEntry( 092 s[0], 093 s[1], 094 s[2], 095 Integer.parseInt(s[3]), 096 s[4], 097 s[5], 098 synonyms 099 ); 100 System.out.println("Entry: " + entry); 101 entries.add(entry); 102 } 103 return entries.stream(); 104 } catch (URISyntaxException ex) { 105 Logger.getLogger(LipidClassGenerator.class.getName()).log(Level.SEVERE, null, ex); 106 } 107 return Stream.empty(); 108 } 109 110 public String sanitizeToEnumConstant(String lipidName, String lipidCategory) { 111 String enumName = lipidName.replaceAll("[/\\-\\s(),.\\[\\]:;]", "_").replaceAll("'", "p") 112 .replaceAll("^([_0-9]+)", lipidCategory + "_$1").replaceAll("[_]+", "_").toUpperCase(); 113 if(enumName.endsWith("_")) { 114 return enumName.substring(0, enumName.length()-1); 115 } 116 return enumName; 117 } 118 119 public String getEnumFromTable(Stream<LipidClassEntry> stream) { 120 final Builder lipidClassBuilder = TypeSpec.enumBuilder("LipidClass").addJavadoc( 121 "Enumeration of lipid classes. The shorthand names / abbreviations are used to\n" 122 + "look up the lipid class association of a lipid head group. We try to map each\n" 123 + "abbreviation and synonyms thereof to LipidMAPS main class. However, not all\n" 124 + "described head groups are categorized in LipidMAPS, or only occur in other\n" 125 + "databases, so they do not have such an association at the moment.\n" 126 + "\n" 127 + "Example: Category=Glyerophospholipids maps to Class=Glycerophosphoinositols (PI)\n" 128 + "\n" 129 + "@author nils.hoffmann" 130 ). 131 addModifiers(Modifier.PUBLIC); 132 lipidClassBuilder.addField(LipidCategory.class, "category", Modifier.PRIVATE, Modifier.FINAL); 133 lipidClassBuilder.addField(String.class, "lipidMapsClassName", Modifier.PRIVATE, Modifier.FINAL); 134 ClassName synonymsClass = ClassName.get("java.lang", "String"); 135 ClassName integersClass = ClassName.get("java.lang", "Integer"); 136 ClassName list = ClassName.get("java.util", "List"); 137 lipidClassBuilder.addField(String.class, "allowedNumFaStr", Modifier.PRIVATE, Modifier.FINAL); 138 TypeName listOfIntegers = ParameterizedTypeName.get(list, integersClass); 139 lipidClassBuilder.addField(listOfIntegers, "allowedNumFa", Modifier.PRIVATE, Modifier.FINAL); 140 lipidClassBuilder.addField(Integer.class, "maxNumFa", Modifier.PRIVATE, Modifier.FINAL); 141 lipidClassBuilder.addField(String.class, "sumFormula", Modifier.PRIVATE, Modifier.FINAL); 142 lipidClassBuilder.addField(ElementTable.class, "elementTable", Modifier.PRIVATE, Modifier.FINAL); 143 ClassName arrayList = ClassName.get("java.util", "ArrayList"); 144 TypeName listOfSynonyms = ParameterizedTypeName.get(list, synonymsClass); 145 lipidClassBuilder.addField(listOfSynonyms, "synonyms", Modifier.PRIVATE, Modifier.FINAL); 146 TypeName optionalLipidClass = ParameterizedTypeName.get(Optional.class, LipidClass.class); 147 148 lipidClassBuilder.addMethod( 149 MethodSpec.constructorBuilder(). 150 addModifiers(Modifier.PRIVATE). 151 addParameter(LipidCategory.class, "category"). 152 addStatement("this.$N = $N", "category", "category"). 153 addParameter(String.class, "lipidMapsClassName"). 154 addStatement("this.$N = $N", "lipidMapsClassName", "lipidMapsClassName"). 155 addParameter(Integer.class, "maxNumFa"). 156 addStatement("this.$N = $N", "maxNumFa", "maxNumFa"). 157 addParameter(String.class, "allowedNumFaStr"). 158 addStatement("this.$N = $N", "allowedNumFaStr", "allowedNumFaStr"). 159 addParameter(String.class, "sumFormula"). 160 addStatement("this.$N = $N", "sumFormula", "sumFormula"). 161 addStatement( 162 CodeBlock.of( 163 "ElementTable et = new ElementTable();\n" 164 + " if(this.sumFormula!=null && !this.sumFormula.isEmpty()) {\n" 165 + " try {\n" 166 + " et = new ElementTable(this.sumFormula);\n" 167 + " } catch (de.isas.lipidomics.palinom.exceptions.ParsingException ex) {\n" 168 + " et = new ElementTable();\n" 169 + " }\n" 170 + " }\n" 171 + " this.elementTable = et;" 172 ) 173 ). 174 addParameter(listOfSynonyms, "synonyms"). 175 addStatement( 176 CodeBlock.of( 177 "if ($N.isEmpty()) {\n" 178 + " throw new IllegalArgumentException(\"Must supply at least one synonym!\");\n" 179 + "}", "synonyms") 180 ). 181 addStatement( 182 CodeBlock.of( 183 "this.$N = Arrays.asList(allowedNumFaStr.split(\"\\\\|\")).stream().map((t) -> {\n" 184 + " return Integer.parseInt(t);\n" 185 + "}).collect(java.util.stream.Collectors.toList())", "allowedNumFa") 186 ). 187 addStatement("this.$N = $N", "synonyms", "synonyms"). 188 build() 189 ); 190 191 lipidClassBuilder.addMethod( 192 MethodSpec.methodBuilder("getCategory").addModifiers(Modifier.PUBLIC).returns(LipidCategory.class).addCode("return this.$N;", "category").build() 193 ); 194 lipidClassBuilder.addMethod( 195 MethodSpec.methodBuilder("getAbbreviation").addModifiers(Modifier.PUBLIC).returns(String.class).addCode("return this.$N.get(0);", "synonyms").build() 196 ); 197 lipidClassBuilder.addMethod( 198 MethodSpec.methodBuilder("getLipidMapsClassName").addModifiers(Modifier.PUBLIC).returns(String.class).addCode("return this.$N;", "lipidMapsClassName").build() 199 ); 200 lipidClassBuilder.addMethod( 201 MethodSpec.methodBuilder("getMaxNumFa").addModifiers(Modifier.PUBLIC).returns(Integer.class).addCode("return this.$N;", "maxNumFa").build() 202 ); 203 lipidClassBuilder.addMethod( 204 MethodSpec.methodBuilder("getAllowedNumFa").addModifiers(Modifier.PUBLIC).returns(listOfIntegers).addCode("return this.$N;", "allowedNumFa").build() 205 ); 206 lipidClassBuilder.addMethod( 207 MethodSpec.methodBuilder("getSumFormula").addModifiers(Modifier.PUBLIC).returns(String.class).addCode("return this.$N.getSumFormula();", "elementTable").build() 208 ); 209 lipidClassBuilder.addMethod( 210 MethodSpec.methodBuilder("getElements").addModifiers(Modifier.PUBLIC).returns(ElementTable.class).addCode("return this.$N.copy();", "elementTable").build() 211 ); 212 lipidClassBuilder.addMethod( 213 MethodSpec.methodBuilder("getSynonyms").addModifiers(Modifier.PUBLIC).returns(listOfSynonyms).addCode("return this.$N;", "synonyms").build() 214 ); 215 216 lipidClassBuilder.addMethod( 217 MethodSpec.methodBuilder("matchesAbbreviation"). 218 addModifiers(Modifier.PUBLIC). 219 addParameter(String.class, "headGroup"). 220 addStatement( 221 CodeBlock.of( 222 "return this.synonyms.stream().anyMatch((synonym) -> {\n" 223 + "return synonym.equals($N);\n" 224 + "})", "headGroup")). 225 returns(boolean.class). 226 build() 227 ); 228 229 lipidClassBuilder.addMethod( 230 MethodSpec.methodBuilder("getLysoAbbreviation"). 231 addModifiers(Modifier.PUBLIC). 232 addParameter(LipidClass.class, "lipidClass"). 233 addStatement( 234 CodeBlock.of( 235 "if ($N.getCategory() == LipidCategory.GP) { " 236 + "return \"L\" + $N.getAbbreviation();\n" 237 + "}\n" 238 + "throw new ConstraintViolationException(\"Lipid category must be \" + LipidCategory.GP + \" for lyso-classes!\")", "lipidClass", "lipidClass")). 239 returns(String.class). 240 build() 241 ); 242 243 lipidClassBuilder.addMethod( 244 MethodSpec.methodBuilder("forHeadGroup"). 245 addModifiers(Modifier.PUBLIC, Modifier.STATIC). 246 addParameter(String.class, "headGroup"). 247 addStatement( 248 CodeBlock.of( 249 "return Arrays.asList(values()).stream().filter((lipidClass) -> {\n" 250 + " return lipidClass.matchesAbbreviation($N.trim());\n" 251 + "}).findFirst()", "headGroup")). 252 returns(optionalLipidClass). 253 build() 254 ); 255 256 stream.forEach((lipidClassEntry) -> { 257 String sanitizedName = sanitizeToEnumConstant(lipidClassEntry.getLipidName(), lipidClassEntry.getLipidCategory()); 258 LipidCategory category = LipidCategory.valueOf(lipidClassEntry.getLipidCategory()); 259 List<String> template = new ArrayList<>(); 260 template.addAll( 261 Arrays.asList( 262 "LipidCategory." + category.name(), 263 "\"" + lipidClassEntry.lipidDescription + "\"", 264 lipidClassEntry.maxNumFa + "", 265 "\"" + lipidClassEntry.allowedNumFa + "\"", 266// "\"" + lipidClassEntry.lipidName + "\"", 267 "\"" + lipidClassEntry.sumFormula + "\"" 268 ) 269 ); 270 List<String> synonyms = new ArrayList<>(); 271 synonyms.add("\"" + lipidClassEntry.lipidName + "\""); 272 synonyms.addAll(lipidClassEntry.getSynonyms().stream().map((t) -> { 273 return "\"" + t + "\""; 274 }).collect(Collectors.toList())); 275 template.add("java.util.Arrays.asList(" + synonyms.stream().collect(Collectors.joining(",")) + ")"); 276 CodeBlock cb = CodeBlock.of(String.join(", ", template)); 277 lipidClassBuilder.addEnumConstant(sanitizedName, TypeSpec.anonymousClassBuilder(cb).build()); 278 }); 279 280 return JavaFile.builder("de.isas.lipidomics.domain", lipidClassBuilder.build()).addFileComment( 281 " Copyright 2019 nils.hoffmann.\n" 282 + "\n" 283 + " Licensed under the Apache License, Version 2.0 (the \"License\");\n" 284 + " you may not use this file except in compliance with the License.\n" 285 + " You may obtain a copy of the License at\n" 286 + "\n" 287 + " http://www.apache.org/licenses/LICENSE-2.0\n" 288 + "\n" 289 + " Unless required by applicable law or agreed to in writing, software\n" 290 + " distributed under the License is distributed on an \"AS IS\" BASIS,\n" 291 + " WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n" 292 + " See the License for the specific language governing permissions and\n" 293 + " limitations under the License." 294 ).build().toString(); 295 } 296}