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 de.isas.mztab2.io.serialization;
017
018import com.fasterxml.jackson.annotation.JsonProperty;
019import com.fasterxml.jackson.core.JsonGenerator;
020import com.fasterxml.jackson.databind.ObjectMapper;
021import com.fasterxml.jackson.databind.SerializerProvider;
022import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
023import de.isas.mztab2.model.Assay;
024import de.isas.mztab2.model.IndexedElement;
025import de.isas.mztab2.model.OptColumnMapping;
026import de.isas.mztab2.model.Parameter;
027import de.isas.mztab2.model.StudyVariable;
028import de.isas.mztab2.model.Uri;
029import java.io.IOException;
030import java.lang.reflect.InvocationTargetException;
031import java.util.Arrays;
032import java.util.Collections;
033import java.util.List;
034import java.util.Map;
035import java.util.Optional;
036import java.util.logging.Level;
037import java.util.logging.Logger;
038import java.util.regex.Matcher;
039import java.util.regex.Pattern;
040import java.util.stream.Collectors;
041import java.util.stream.IntStream;
042import javax.validation.ValidationException;
043import lombok.extern.slf4j.Slf4j;
044import uk.ac.ebi.pride.jmztab2.model.IMZTabColumn;
045import uk.ac.ebi.pride.jmztab2.model.MZTabConstants;
046import static uk.ac.ebi.pride.jmztab2.model.MZTabConstants.NULL;
047import uk.ac.ebi.pride.jmztab2.model.MetadataElement;
048import uk.ac.ebi.pride.jmztab2.model.MetadataProperty;
049
050/**
051 * <p>
052 * Utility class providing helper methods for other serializers.</p>
053 *
054 * @author nilshoffmann
055 * @since 11/30/17
056 *
057 */
058@Slf4j
059public class Serializers {
060
061    /**
062     * <p>
063     * getReference.</p>
064     *
065     * @param element a {@link java.lang.Object} object.
066     * @param idx a {@link java.lang.Integer} object.
067     * @return a {@link java.lang.String} object.
068     */
069    public static String getReference(Object element, Integer idx) {
070        StringBuilder sb = new StringBuilder();
071        sb.append(getElementName(element).
072                orElseThrow(()
073                        -> {
074                    return new IllegalArgumentException(
075                            "No mzTab element name mapping available for " + element.
076                                    getClass().
077                                    getName());
078                }));
079
080        return sb.toString();
081    }
082
083    /**
084     * <p>
085     * printAbundanceAssay.</p>
086     *
087     * @param a a {@link de.isas.mztab2.model.Assay} object.
088     * @return a {@link java.lang.String} object.
089     */
090    public static String printAbundanceAssay(Assay a) {
091        StringBuilder sb = new StringBuilder();
092        return sb.append("abundance_assay[").
093                append(a.getId()).
094                append("]").
095                toString();
096    }
097
098    /**
099     * <p>
100     * printAbundanceStudyVar.</p>
101     *
102     * @param sv a {@link de.isas.mztab2.model.StudyVariable} object.
103     * @return a {@link java.lang.String} object.
104     */
105    public static String printAbundanceStudyVar(StudyVariable sv) {
106        StringBuilder sb = new StringBuilder();
107        return sb.append("abundance_study_variable[").
108                append(sv.getId()).
109                append("]").
110                toString();
111    }
112
113    /**
114     * <p>
115     * printAbundanceCoeffVarStudyVar.</p>
116     *
117     * @param sv a {@link de.isas.mztab2.model.StudyVariable} object.
118     * @return a {@link java.lang.String} object.
119     */
120    public static String printAbundanceCoeffVarStudyVar(StudyVariable sv) {
121        StringBuilder sb = new StringBuilder();
122        return sb.append("abundance_coeffvar_study_variable[").
123                append(sv.getId()).
124                append("]").
125                toString();
126    }
127
128    /**
129     * <p>
130     * printOptColumnMapping.</p>
131     *
132     * @param ocm a {@link de.isas.mztab2.model.OptColumnMapping} object.
133     * @return a {@link java.lang.String} object.
134     */
135    public static String printOptColumnMapping(OptColumnMapping ocm) {
136        StringBuilder sb = new StringBuilder();
137        log.debug("Identifier={}; OptColumnMapping: {}", ocm.getIdentifier(), ocm);
138        if ("global".equals(ocm.getIdentifier()) || ocm.getIdentifier().startsWith("global")) {
139            sb.append("opt_");
140            sb.append(ocm.getIdentifier());
141            if (ocm.getParam() != null) {
142                sb.append("_cv_").
143                        append(ocm.getParam().
144                                getCvAccession()).
145                        append("_").
146                        append(ocm.getParam().
147                                getName().
148                                replaceAll(" ", "_"));
149            }
150        } else {
151            log.debug("OptColumnMapping: {}", ocm);
152            // object reference case, value is now the actual value
153            sb.append(ocm.getIdentifier());
154        }
155        log.debug("asString: {}", sb.toString());
156//        //TODO: check for valid characters in definition
157//        //valid characters: ‘A’-‘Z’, ‘a’-‘z’, ‘0’-‘9’, ‘’, ‘-’, ‘[’, ‘]’, and ‘:’.
158//        //[A-Za-z0-9\[\]-:]+
159        return sb.toString();
160    }
161
162    /**
163     * <p>
164     * addIndexedLine for elements like assay[1] that have an id and one
165     * additional property element</p>
166     *
167     * @param <T> the type of {@link IndexedElement}.
168     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
169     * @param sp a {@link com.fasterxml.jackson.databind.SerializerProvider}
170     * object.
171     * @param prefix a {@link java.lang.String} object.
172     * @param element a {@link java.lang.Object} object.
173     * @param indexedElement a {@link de.isas.mztab2.model.Parameter} object.
174     */
175    public static <T extends Object> void addIndexedLine(
176            JsonGenerator jg, SerializerProvider sp, String prefix,
177            Object element, T indexedElement) {
178        addIndexedLine(jg, sp, prefix, element, Arrays.asList(indexedElement));
179    }
180
181    /**
182     * <p>
183     * addIndexedLine for elements like assay[1] that have an id and a list of
184     * additional property elements</p>
185     *
186     * @param <T> the type of {@link IndexedElement}.
187     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
188     * @param sp a {@link com.fasterxml.jackson.databind.SerializerProvider}
189     * object.
190     * @param prefix a {@link java.lang.String} object.
191     * @param element a {@link java.lang.Object} object.
192     * @param indexedElementList a {@link java.util.List} object.
193     */
194    public static <T extends Object> void addIndexedLine(
195            JsonGenerator jg, SerializerProvider sp, String prefix,
196            Object element,
197            List<T> indexedElementList) {
198        Optional<List<T>> iel = Optional.ofNullable(indexedElementList);
199        if (!iel.isPresent() || indexedElementList.isEmpty()) {
200
201            log.debug(
202                    "Skipping null or empty indexed element list values for {}",
203                    getElementName(
204                            element));
205            return;
206        }
207        try {
208            jg.writeStartArray();
209            //prefix
210            jg.writeString(prefix);
211            //key
212            jg.writeString(new StringBuilder().append(getElementName(element).
213                    orElseThrow(()
214                            -> {
215                        return new ElementNameMappingException("unknown", element);
216                    })).
217                    toString());
218            //value
219            jg.writeString(indexedElementList.stream().
220                    map((indexedElement)
221                            -> {
222                        if (indexedElement instanceof Parameter) {
223                            return new ParameterConverter().convert(
224                                    (Parameter) indexedElement);
225                        } else if (indexedElement instanceof Uri) {
226                            return new UriConverter().convert((Uri) indexedElement);
227                        } else {
228                            throw new IllegalArgumentException(
229                                    "Serialization of type " + indexedElement.getClass() + " currently not supported!");
230                        }
231                    }).
232                    collect(Collectors.joining("|")));
233            jg.writeEndArray();
234        } catch (IOException ex) {
235
236            log.error("Caught IO Exception while trying to write indexed line:",
237                    ex);
238        }
239    }
240
241    /**
242     * <p>
243     * addLineWithParameters.</p>
244     *
245     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
246     * @param prefix a {@link java.lang.String} object.
247     * @param element a {@link java.lang.Object} object.
248     * @param parameterList a {@link java.util.List} object.
249     */
250    public static void addLineWithParameters(JsonGenerator jg, String prefix,
251            Object element,
252            List<Parameter> parameterList) {
253        if (parameterList == null || parameterList.isEmpty()) {
254
255            log.debug(
256                    "Skipping null or empty parameter list values for " + getElementName(
257                            element));
258            return;
259        }
260        try {
261            jg.writeStartArray();
262            //prefix
263            jg.writeString(prefix);
264            //key
265            jg.writeString(new StringBuilder().append(getElementName(element).
266                    orElseThrow(()
267                            -> {
268                        return new ElementNameMappingException("unknown", element);
269                    })).
270                    toString());
271            //value
272            jg.writeString(parameterList.stream().
273                    map((parameter)
274                            -> {
275                        return new ParameterConverter().convert(parameter);
276                    }).
277                    collect(Collectors.joining("|")));
278            jg.writeEndArray();
279        } catch (IOException ex) {
280            log.error(
281                    "Caught IO Exception while trying to write line with parameters:",
282                    ex);
283        }
284    }
285
286    /**
287     * <p>
288     * addLineWithPropertyParameters.</p>
289     *
290     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
291     * @param prefix a {@link java.lang.String} object.
292     * @param propertyName a {@link java.lang.String} object.
293     * @param element a {@link java.lang.Object} object.
294     * @param value a {@link java.util.List} object.
295     */
296    public static void addLineWithPropertyParameters(JsonGenerator jg,
297            String prefix,
298            String propertyName, Object element,
299            List<Parameter> value) {
300        if (value == null || value.isEmpty()) {
301
302            log.debug("Skipping null or empty values for {}",
303                    getElementName(
304                            element));
305            return;
306        }
307        addLineWithProperty(jg, prefix, propertyName, element, value.stream().
308                map((parameter)
309                        -> {
310                    return new ParameterConverter().convert(parameter);
311                }).
312                collect(Collectors.joining("|")));
313    }
314
315    /**
316     * <p>
317     * addLineWithMetadataProperty.</p>
318     *
319     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
320     * @param prefix a {@link java.lang.String} object.
321     * @param property a {@link uk.ac.ebi.pride.jmztab2.model.MetadataProperty}
322     * object.
323     * @param element a {@link java.lang.Object} object.
324     * @param value a {@link java.lang.Object} object.
325     */
326    public static void addLineWithMetadataProperty(JsonGenerator jg,
327            String prefix, MetadataProperty property, Object element,
328            Object... value) {
329        addLineWithProperty(jg, prefix, property.getName(), element, value);
330    }
331
332    /**
333     * <p>
334     * addLineWithNullProperty.</p>
335     *
336     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
337     * @param prefix a {@link java.lang.String} object.
338     * @param propertyName a {@link java.lang.String} object.
339     * @param element a {@link java.lang.Object} object.
340     */
341    public static void addLineWithNullProperty(JsonGenerator jg, String prefix,
342            String propertyName, Object element) {
343        try {
344            jg.writeStartArray();
345            //prefix
346            jg.writeString(prefix);
347            //key
348            String key = getElementName(element).
349                    orElseThrow(()
350                            -> {
351                        return new ElementNameMappingException(propertyName, element);
352                    });
353            if (propertyName == null) {
354                jg.writeString(key);
355            } else {
356                jg.writeString(key + "-" + propertyName);
357            }
358            //value
359            jg.writeString(NULL);
360            jg.writeEndArray();
361        } catch (IOException ex) {
362
363            log.error(
364                    "Caught exception while trying to write line with null property:",
365                    ex);
366        }
367    }
368
369    /**
370     * <p>
371     * addLineWithProperty.</p>
372     *
373     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
374     * @param prefix a {@link java.lang.String} object.
375     * @param propertyName a {@link java.lang.String} object.
376     * @param element a {@link java.lang.Object} object.
377     * @param value a {@link java.lang.Object} object.
378     */
379    public static void addLineWithProperty(JsonGenerator jg, String prefix,
380            String propertyName, Object element,
381            Object... value) {
382        if (value == null || value.length == 0) {
383
384            log.debug("Skipping null or empty values for {}",
385                    getElementName(
386                            element));
387            return;
388        }
389        if (value.length == 1 && (value[0] == null)) {
390
391            log.debug("Skipping empty value for {}", getElementName(
392                    element));
393            return;
394        }
395        try {
396            jg.writeStartArray();
397            //prefix
398            jg.writeString(prefix);
399            //key
400            String key = getElementName(element).
401                    orElseThrow(()
402                            -> {
403                        return new ElementNameMappingException(propertyName, element);
404                    });
405            if (propertyName == null) {
406                jg.writeString(key);
407            } else {
408                jg.writeString(key + "-" + propertyName);
409            }
410            //value
411            for (Object o : value) {
412                jg.writeObject(o);
413            }
414            jg.writeEndArray();
415        } catch (IOException ex) {
416
417            log.error(
418                    "Caught IO exception while trying to write line with property:",
419                    ex);
420        }
421    }
422
423    /**
424     * <p>
425     * addLine.</p>
426     *
427     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
428     * @param prefix a {@link java.lang.String} object.
429     * @param element a {@link java.lang.Object} object.
430     * @param value a {@link java.lang.Object} object.
431     */
432    public static void addLine(JsonGenerator jg, String prefix, Object element,
433            Object... value) {
434        addLineWithProperty(jg, prefix, null, element, value);
435    }
436
437    /**
438     * <p>
439     * getElementName.</p>
440     *
441     * @param element a {@link java.lang.Object} object.
442     * @return a {@link java.util.Optional} object.
443     */
444    public static Optional<String> getElementName(Object element) {
445        if (element instanceof String) {
446            return Optional.of((String) element);
447        }
448        if (element instanceof MetadataElement) {
449            return Optional.ofNullable(((MetadataElement) element).getName());
450        }
451        JacksonXmlRootElement rootElement = element.getClass().
452                getAnnotation(JacksonXmlRootElement.class);
453        if (rootElement != null) {
454            String underscoreName = camelCaseToUnderscoreLowerCase(
455                    rootElement.localName());
456            if (element instanceof IndexedElement) {
457                Integer id = Optional.ofNullable(((IndexedElement) element).getId()).orElseThrow(()
458                        -> new NullPointerException(
459                                "Field 'id' must not be null for element '" + underscoreName + "'!")
460                );
461                return Optional.of(
462                        underscoreName + "[" + id + "]");
463            } 
464            IndexedElement ielement = IndexedElement.of(element);
465            if(ielement!=null) {
466                Integer id = Optional.ofNullable((ielement).getId()).orElseThrow(()
467                        -> new NullPointerException(
468                                "Field 'id' must not be null for element '" + underscoreName + "'!")
469                );
470                return Optional.of(
471                        underscoreName + "[" + id + "]");
472            }
473                
474            return Optional.ofNullable(underscoreName);
475        }
476        return Optional.empty();
477    }
478
479    /**
480     * <p>
481     * getPropertyNames.</p>
482     *
483     * @param element a {@link java.lang.Object} object.
484     * @return a {@link java.util.List} object.
485     */
486    public static List<String> getPropertyNames(Object element) {
487        return Arrays.asList(element.getClass().
488                getAnnotationsByType(JsonProperty.class)).
489                stream().
490                map((jsonProperty)
491                        -> {
492                    return jsonProperty.value();
493                }).
494                collect(Collectors.toList());
495    }
496
497    /**
498     * <p>
499     * asMap.</p>
500     *
501     * @param element a {@link java.lang.Object} object.
502     * @return a {@link java.util.Map} object.
503     */
504    public static Map<String, Object> asMap(Object element) {
505        ObjectMapper objectMapper = new ObjectMapper();
506        return objectMapper.convertValue(element, Map.class);
507    }
508
509    /**
510     * <p>
511     * camelCaseToUnderscoreLowerCase.</p>
512     *
513     * @param camelCase a {@link java.lang.String} object.
514     * @return a {@link java.lang.String} object.
515     */
516    public static String camelCaseToUnderscoreLowerCase(String camelCase) {
517        Matcher m = Pattern.compile("(?<=[a-z])[A-Z]").
518                matcher(camelCase);
519
520        StringBuffer sb = new StringBuffer();
521        while (m.find()) {
522            m.appendReplacement(sb, "_" + m.group().
523                    toLowerCase());
524        }
525        m.appendTail(sb);
526        return sb.toString().
527                toLowerCase();
528    }
529
530    /**
531     * <p>
532     * addSubElementStrings.</p>
533     *
534     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
535     * @param prefix a {@link java.lang.String} object.
536     * @param element a {@link java.lang.Object} object.
537     * @param subElementName a {@link java.lang.String} object.
538     * @param subElements a {@link java.util.List} object.
539     * @param oneLine a boolean.
540     */
541    public static void addSubElementStrings(JsonGenerator jg, String prefix,
542            Object element, String subElementName, List<?> subElements,
543            boolean oneLine) {
544        if (checkForNull(element, subElements, subElementName)) {
545            return;
546        }
547        Serializers.getElementName(element).
548                ifPresent((elementName)
549                        -> {
550                    if (oneLine) {
551                        addLine(jg, prefix,
552                                elementName + "-" + subElementName,
553                                subElements.stream().
554                                        map((t)
555                                                -> {
556                                            return t.toString();
557                                        }).
558                                        collect(Collectors.joining("" + MZTabConstants.BAR)));
559                    } else {
560                        IntStream.range(0, subElements.
561                                size()).
562                                forEachOrdered(i
563                                        -> {
564                                    addLine(jg, prefix,
565                                            elementName + "-" + subElementName + "[" + (i + 1) + "]",
566                                            subElements.
567                                                    get(i));
568                                });
569                    }
570                });
571
572    }
573
574    /**
575     * <p>
576     * addSubElementParameter.</p>
577     *
578     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
579     * @param prefix a {@link java.lang.String} object.
580     * @param element a {@link java.lang.Object} object.
581     * @param subElementName a {@link java.lang.String} object.
582     * @param subElement a {@link de.isas.mztab2.model.Parameter} object.
583     */
584    public static void addSubElementParameter(JsonGenerator jg, String prefix,
585            Object element, String subElementName, Parameter subElement) {
586        if (subElement == null) {
587            String elementName = Serializers.getElementName(element).
588                    orElse("undefined");
589
590            log.debug("''{}-{}'' is null or empty!", new Object[]{
591                elementName,
592                subElementName});
593            return;
594        }
595        addSubElementStrings(jg, prefix, element, subElementName, Arrays.asList(
596                new ParameterConverter().convert(subElement)), true);
597    }
598
599    /**
600     * <p>
601     * addSubElementParameters.</p>
602     *
603     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
604     * @param prefix a {@link java.lang.String} object.
605     * @param element a {@link java.lang.Object} object.
606     * @param subElementName a {@link java.lang.String} object.
607     * @param subElements a {@link java.util.List} object.
608     * @param oneLine a boolean.
609     */
610    public static void addSubElementParameters(JsonGenerator jg, String prefix,
611            Object element, String subElementName, List<Parameter> subElements,
612            boolean oneLine) {
613        if (checkForNull(element, subElements, subElementName)) {
614            return;
615        }
616        addSubElementStrings(jg, prefix, element, subElementName,
617                subElements.stream().
618                        map((parameter)
619                                -> {
620                            try {
621                                return new ParameterConverter().convert(parameter);
622                            } catch (IllegalArgumentException npe) {
623
624                                log.debug("parameter is null for {}",
625                                        subElementName);
626                                return "null";
627                            }
628                        }).
629                        collect(Collectors.toList()), oneLine);
630    }
631
632    /**
633     * <p>
634     * checkForNull.</p>
635     *
636     * @param element a {@link java.lang.Object} object.
637     * @param subElements a {@link java.util.List} object.
638     * @param subElementName a {@link java.lang.String} object.
639     * @return a boolean.
640     */
641    public static boolean checkForNull(Object element, List<?> subElements,
642            String subElementName) {
643        String elementName = Serializers.getElementName(element).
644                orElse("undefined");
645        if (subElements == null || subElements.isEmpty()) {
646
647            log.debug("''{}-{}'' is null or empty!", new Object[]{
648                elementName,
649                subElementName});
650            return true;
651        }
652        return false;
653    }
654
655    /**
656     * <p>
657     * writeString.</p>
658     *
659     * @param columnName a {@link java.lang.String} object.
660     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
661     * @param value a {@link java.lang.String} object.
662     * @throws java.io.IOException if an operation on the JsonGenerator object
663     * fails.
664     */
665    public static void writeString(String columnName, JsonGenerator jg,
666            String value) throws IOException {
667        if (value == null) {
668            jg.writeNullField(columnName);
669        } else {
670            jg.writeStringField(columnName, value);
671        }
672    }
673
674    /**
675     * <p>
676     * writeString.</p>
677     *
678     * @param column a {@link uk.ac.ebi.pride.jmztab2.model.IMZTabColumn}
679     * object.
680     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
681     * @param value a {@link java.lang.String} object.
682     * @throws java.io.IOException if an operation on the JsonGenerator object
683     * fails.
684     */
685    public static void writeString(IMZTabColumn column, JsonGenerator jg,
686            String value) throws IOException {
687        writeString(column.getHeader(), jg, value);
688    }
689
690    /**
691     * <p>
692     * writeObject.</p>
693     *
694     * @param columnName a {@link java.lang.String} object.
695     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
696     * @param sp a {@link com.fasterxml.jackson.databind.SerializerProvider}
697     * object.
698     * @param value a {@link java.lang.Object} object.
699     * @throws java.io.IOException if an operation on the JsonGenerator object
700     * fails.
701     */
702    public static void writeObject(String columnName, JsonGenerator jg,
703            SerializerProvider sp,
704            Object value) throws IOException {
705        if (value == null) {
706            jg.writeNullField(columnName);
707        } else {
708            if (value instanceof Parameter) {
709                jg.writeStringField(columnName, new ParameterConverter().
710                        convert((Parameter) value));
711            } else if (value instanceof String) {
712                jg.writeStringField(columnName, (String) value);
713            } else {
714                throw new IllegalArgumentException(
715                        "Serialization of objects of type " + value.getClass()
716                        + " currently not supported!");
717            }
718
719        }
720    }
721
722    /**
723     * <p>
724     * writeObject.</p>
725     *
726     * @param column a {@link uk.ac.ebi.pride.jmztab2.model.IMZTabColumn}
727     * object.
728     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
729     * @param sp a {@link com.fasterxml.jackson.databind.SerializerProvider}
730     * object.
731     * @param value a {@link java.lang.Object} object.
732     * @throws java.io.IOException if an operation on the JsonGenerator object
733     * fails.
734     */
735    public static void writeObject(IMZTabColumn column, JsonGenerator jg,
736            SerializerProvider sp, Object value) throws IOException {
737        writeObject(column.getHeader(), jg, sp, value);
738    }
739
740    /**
741     * <p>
742     * writeAsNumberArray.</p>
743     *
744     * @param column a {@link uk.ac.ebi.pride.jmztab2.model.IMZTabColumn}
745     * object.
746     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
747     * @param elements a {@link java.util.List} object.
748     */
749    public static void writeAsNumberArray(IMZTabColumn column, JsonGenerator jg,
750            List<? extends Number> elements) {
751        writeAsNumberArray(column.getHeader(), jg, elements);
752    }
753
754    /**
755     * <p>
756     * writeAsNumberArray.</p>
757     *
758     * @param columnName a {@link java.lang.String} object.
759     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
760     * @param elements a {@link java.util.List} object.
761     */
762    public static void writeAsNumberArray(String columnName, JsonGenerator jg,
763            List<? extends Number> elements) {
764        try {
765            if (elements == null) {
766                jg.writeNullField(columnName);
767            } else {
768                String arrayElements = elements.stream().
769                        map((number)
770                                -> {
771                            if (number == null) {
772                                return MZTabConstants.NULL;
773                            } else if (number instanceof Short) {
774                                return "" + number.shortValue();
775                            } else if (number instanceof Integer) {
776                                return "" + number.intValue();
777                            } else if (number instanceof Long) {
778                                return "" + number.longValue();
779                            } else if (number instanceof Float) {
780                                return "" + number.floatValue();
781                            } else {
782                                return "" + number.doubleValue();
783                            }
784                        }).
785                        collect(Collectors.joining("" + MZTabConstants.BAR));
786                if (arrayElements.isEmpty()) {
787                    jg.writeNullField(columnName);
788                } else {
789                    jg.writeStringField(columnName, arrayElements);
790                }
791            }
792        } catch (IOException ex) {
793            log.error(
794                    "Caught IO exception while trying to write as number array: ",
795                    ex);
796        }
797    }
798
799    /**
800     * <p>
801     * writeAsStringArray.</p>
802     *
803     * @param column a {@link uk.ac.ebi.pride.jmztab2.model.IMZTabColumn}
804     * object.
805     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
806     * @param elements a {@link java.util.List} object.
807     */
808    public static void writeAsStringArray(IMZTabColumn column, JsonGenerator jg,
809            List<String> elements) {
810        writeAsStringArray(column.getHeader(), jg, elements);
811    }
812
813    /**
814     * <p>
815     * writeAsStringArray.</p>
816     *
817     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
818     * @param elements a {@link java.util.List} object.
819     */
820    public static void writeAsStringArray(JsonGenerator jg,
821            List<String> elements) {
822        try {
823            if (elements == null) {
824                jg.writeNull();
825            } else {
826                String arrayElements = elements.stream().
827                        map((t)
828                                -> {
829                            if (t == null) {
830                                return MZTabConstants.NULL;
831                            }
832                            return t;
833                        }).
834                        collect(Collectors.joining("" + MZTabConstants.BAR));
835                if (arrayElements.isEmpty()) {
836                    jg.writeNull();
837                } else {
838                    jg.writeString(arrayElements);
839                }
840            }
841        } catch (IOException ex) {
842            log.error("Error while trying to write as string array:", ex);
843        }
844    }
845
846    /**
847     * <p>
848     * writeAsStringArray.</p>
849     *
850     * @param columnName a {@link java.lang.String} object.
851     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
852     * @param elements a {@link java.util.List} object.
853     */
854    public static void writeAsStringArray(String columnName, JsonGenerator jg,
855            List<String> elements) {
856        try {
857            if (elements == null) {
858                jg.writeNullField(columnName);
859            } else {
860                String arrayElements = elements.stream().
861                        map((t)
862                                -> {
863                            if (t == null) {
864                                return MZTabConstants.NULL;
865                            }
866                            return t;
867                        }).
868                        collect(Collectors.joining("" + MZTabConstants.BAR));
869                if (arrayElements.isEmpty()) {
870                    jg.writeNullField(columnName);
871                } else {
872                    jg.writeStringField(columnName, arrayElements);
873                }
874            }
875        } catch (IOException ex) {
876            log.error("Error while trying to write as string array: ", ex);
877        }
878    }
879
880    /**
881     * <p>
882     * writeNumber.</p>
883     *
884     * @param columnName a {@link java.lang.String} object.
885     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
886     * @param value a {@link java.lang.Integer} object.
887     * @throws java.io.IOException if an operation on the JsonGenerator object
888     * fails.
889     */
890    public static void writeNumber(String columnName, JsonGenerator jg,
891            Integer value) throws IOException {
892        if (value == null) {
893            jg.writeNullField(columnName);
894        } else {
895            jg.writeNumberField(columnName, value);
896        }
897    }
898
899    /**
900     * <p>
901     * writeNumber.</p>
902     *
903     * @param column a {@link uk.ac.ebi.pride.jmztab2.model.IMZTabColumn}
904     * object.
905     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
906     * @param value a {@link java.lang.Integer} object.
907     * @throws java.io.IOException if an operation on the JsonGenerator object
908     * fails.
909     */
910    public static void writeNumber(IMZTabColumn column, JsonGenerator jg,
911            Integer value) throws IOException {
912        writeNumber(column.getHeader(), jg, value);
913    }
914
915    /**
916     * <p>
917     * writeNumber.</p>
918     *
919     * @param columnName a {@link java.lang.String} object.
920     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
921     * @param value a {@link java.lang.Double} object.
922     * @throws java.io.IOException if an operation on the JsonGenerator object
923     * fails.
924     */
925    public static void writeNumber(String columnName, JsonGenerator jg,
926            Double value) throws IOException {
927        if (value == null) {
928            jg.writeNullField(columnName);
929        } else {
930            if (value.equals(Double.NaN)) {
931                jg.writeStringField(columnName, MZTabConstants.CALCULATE_ERROR);
932            } else if (value.equals(Double.POSITIVE_INFINITY)) {
933                jg.writeStringField(columnName, MZTabConstants.INFINITY);
934            } else {
935                jg.writeNumberField(columnName, value);
936            }
937        }
938    }
939
940    /**
941     * <p>
942     * writeNumber.</p>
943     *
944     * @param column a {@link uk.ac.ebi.pride.jmztab2.model.IMZTabColumn}
945     * object.
946     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
947     * @param value a {@link java.lang.Double} object.
948     * @throws java.io.IOException if an operation on the JsonGenerator object
949     * fails.
950     */
951    public static void writeNumber(IMZTabColumn column, JsonGenerator jg,
952            Double value) throws IOException {
953        writeNumber(column.getHeader(), jg, value);
954    }
955
956    /**
957     * <p>
958     * writeNumber.</p>
959     *
960     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
961     * @param value a {@link java.lang.Integer} object.
962     * @throws java.io.IOException if an operation on the JsonGenerator object
963     * fails.
964     */
965    public static void writeNumber(JsonGenerator jg, Integer value) throws IOException {
966        if (value == null) {
967            jg.writeNull();
968        } else {
969            jg.writeNumber(value);
970        }
971    }
972
973    /**
974     * <p>
975     * writeNumber.</p>
976     *
977     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
978     * @param value a {@link java.lang.Double} object.
979     * @throws java.io.IOException if an operation on the JsonGenerator object
980     * fails.
981     */
982    public static void writeNumber(JsonGenerator jg, Double value) throws IOException {
983        if (value == null) {
984            jg.writeNull();
985        } else {
986            jg.writeNumber(value);
987        }
988    }
989
990    /**
991     * <p>
992     * writeOptColumnMappings.</p>
993     *
994     * @param optColumnMappings a {@link java.util.List} object.
995     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
996     * @param sp the serialization provider.
997     * @throws java.io.IOException if an operation on the JsonGenerator object
998     * fails.
999     */
1000    public static void writeOptColumnMappings(
1001            List<OptColumnMapping> optColumnMappings,
1002            JsonGenerator jg, SerializerProvider sp) throws IOException {
1003        for (OptColumnMapping ocm : Optional.ofNullable(
1004                optColumnMappings).
1005                orElse(Collections.emptyList())) {
1006            // write global or indexed element param objects
1007            if (ocm.getParam() != null) {
1008                writeObject(Serializers.printOptColumnMapping(ocm), jg, sp, ocm.
1009                        getValue() == null
1010                                ? (ocm.getParam().getValue() == null || ocm.getParam().getValue().isEmpty()
1011                                ? NULL : ocm.getParam().getValue()) : ocm.getValue());
1012            } else { // write global opt column objects with the value
1013                writeObject(Serializers.printOptColumnMapping(ocm), jg, sp, ocm.
1014                        getValue() == null ? NULL : ocm.getValue());
1015            }
1016        }
1017    }
1018
1019    /**
1020     * <p>
1021     * writeIndexedValues.</p>
1022     *
1023     * @param prefix a {@link java.lang.String} object.
1024     * @param jg a {@link com.fasterxml.jackson.core.JsonGenerator} object.
1025     * @param values a {@link java.util.List} object.
1026     */
1027    public static void writeIndexedDoubles(String prefix,
1028            JsonGenerator jg, List<Double> values) {
1029        IntStream.range(0, values.
1030                size()).
1031                forEachOrdered(i
1032                        -> {
1033                    try {
1034                        Serializers.writeNumber(
1035                                prefix + "[" + (i + 1) + "]",
1036                                jg,
1037                                values.
1038                                        get(i));
1039                    } catch (IOException ex) {
1040                        log.error(
1041                                "Caught IO exception while trying to write indexed doubles:",
1042                                ex);
1043                    }
1044                });
1045    }
1046
1047    public static void checkIndexedElement(Object element) {
1048        Integer id = IndexedElement.of(element).getId();
1049        if (id == null) {
1050            throw new ValidationException(
1051                    "'id' field of " + element.toString() + " must not be null!");
1052        }
1053        if (id < 1) {
1054            throw new ValidationException(
1055                    "'id' field of " + element.toString() + " must have a value greater to equal to 1!");
1056        }
1057    }
1058
1059}