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.utils.parser;
017
018import de.isas.mztab2.model.Assay;
019import de.isas.mztab2.model.CV;
020import de.isas.mztab2.model.ColumnParameterMapping;
021import de.isas.mztab2.model.Contact;
022import de.isas.mztab2.model.Database;
023import de.isas.mztab2.model.Instrument;
024import de.isas.mztab2.model.Metadata;
025import de.isas.mztab2.model.MsRun;
026import de.isas.mztab2.model.Parameter;
027import de.isas.mztab2.model.Publication;
028import de.isas.mztab2.model.PublicationItem;
029import de.isas.mztab2.model.Sample;
030import de.isas.mztab2.model.SampleProcessing;
031import de.isas.mztab2.model.Software;
032import de.isas.mztab2.model.StudyVariable;
033import java.net.URI;
034import java.util.ArrayList;
035import java.util.Collection;
036import java.util.HashMap;
037import java.util.List;
038import java.util.Map;
039import java.util.SortedMap;
040import java.util.TreeMap;
041import lombok.Data;
042import static uk.ac.ebi.pride.jmztab2.model.MZTabStringUtils.isEmpty;
043
044/**
045 * <p>MZTabParserContext is used to keep track of indexed elements and interrelations during parsing.</p>
046 *
047 * @author nilshoffmann
048 * @since 11/09/17
049 * 
050 */
051@Data
052public class MZTabParserContext {
053    private SortedMap<Integer, SampleProcessing> sampleProcessingMap = new TreeMap<>(); 
054    private SortedMap<Integer, Instrument> instrumentMap = new TreeMap<>(); 
055    private SortedMap<Integer, Software> softwareMap = new TreeMap<>(); 
056
057    private SortedMap<Integer, Publication> publicationMap = new TreeMap<>(); 
058    private SortedMap<Integer, Contact> contactMap = new TreeMap<>();
059    private Parameter quantificationMethod; 
060    
061    private SortedMap<Integer, Assay> assayMap = new TreeMap<>(); 
062    private Parameter smallMoleculeQuantificationUnit;
063    private SortedMap<Integer, MsRun> msRunMap = new TreeMap<>();
064    private SortedMap<Integer, Parameter> customItemMap = new TreeMap<>(); 
065    private SortedMap<Integer, Parameter> derivatizationItemMap = new TreeMap<>();
066    private SortedMap<Integer, Parameter> idConfidenceMeasureMap = new TreeMap<>(); 
067    
068    private SortedMap<Integer, Sample> sampleMap = new TreeMap<>();
069    private SortedMap<Integer, StudyVariable> studyVariableMap = new TreeMap<>();
070    private SortedMap<Integer, CV> cvMap = new TreeMap<>();
071    private SortedMap<Integer, Database> databaseMap = new TreeMap<>();
072    private List<ColumnParameterMapping> smallMoleculeColUnitList = new ArrayList<>();
073    private List<ColumnParameterMapping> smallMoleculeFeatureColUnitList = new ArrayList<>();
074    private List<ColumnParameterMapping> smallMoleculeEvidenceColUnitList = new ArrayList<>();
075    private Map<String, String> colUnitMap = new HashMap<>();
076    
077    /**
078     * Add a sample to metadata. Samples are NOT MANDATORY in mzTab, since many software packages cannot determine what
079     * type of sample was analysed (e.g. whether biological or technical replication was performed).
080     *
081     * @param sample SHOULD NOT be null.
082     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
083     * @return a {@link de.isas.mztab2.model.Sample} object.
084     */
085    public Sample addSample(Metadata metadata, Sample sample) {
086        if (sample == null) {
087            throw new IllegalArgumentException("Sample should not be null");
088        }
089
090        this.sampleMap.put(sample.getId(), sample);
091        metadata.addSampleItem(sample);
092        return sample;
093    }
094
095    /**
096     * Add a sample[id]-species into sample.
097     *
098     * @param id SHOULD be positive integer.
099     * @param species if null ignore operation.
100     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
101     * @return a {@link de.isas.mztab2.model.Sample} object.
102     */
103    public Sample addSampleSpecies(Metadata metadata, Integer id, Parameter species) {
104        if (id <= 0) {
105            throw new IllegalArgumentException("Sample id should be greater than 0!");
106        }
107        Sample sample = sampleMap.get(id);
108        if (species == null) {
109            return sample;
110        }
111
112        if (sample == null) {
113            sample = new Sample();
114            sample.id(id);
115            sample.addSpeciesItem(species);
116            sampleMap.put(id, sample);
117            metadata.addSampleItem(sample);
118        } else {
119            sample.addSpeciesItem(species);
120        }
121        return sample;
122    }
123
124    /**
125     * Add a sample[id]-tissue into sample.
126     *
127     * @param id SHOULD be positive integer.
128     * @param tissue if null ignore operation.
129     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
130     * @return a {@link de.isas.mztab2.model.Sample} object.
131     */
132    public Sample addSampleTissue(Metadata metadata, Integer id, Parameter tissue) {
133        if (id <= 0) {
134            throw new IllegalArgumentException("Sample id should be greater than 0!");
135        }
136        Sample sample = sampleMap.get(id);
137        if (tissue == null) {
138            return sample;
139        }
140
141        if (sample == null) {
142            sample = new Sample();
143            sample.id(id);
144            sample.addTissueItem(tissue);
145            sampleMap.put(id, sample);
146            metadata.addSampleItem(sample);
147        } else {
148            sample.addTissueItem(tissue);
149        }
150        return sample;
151    }
152
153    /**
154     * Add a sample[id]-cell_type into sample.
155     *
156     * @param id SHOULD be positive integer.
157     * @param cellType if null ignore operation.
158     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
159     * @return a {@link de.isas.mztab2.model.Sample} object.
160     */
161    public Sample addSampleCellType(Metadata metadata, Integer id, Parameter cellType) {
162        if (id <= 0) {
163            throw new IllegalArgumentException("Sample id should be greater than 0!");
164        }
165        Sample sample = sampleMap.get(id);
166        if (cellType == null) {
167            return sample;
168        }
169
170        if (sample == null) {
171            sample = new Sample();
172            sample.id(id);
173            sample.addCellTypeItem(cellType);
174            sampleMap.put(id, sample);
175            metadata.addSampleItem(sample);
176        } else {
177            sample.addCellTypeItem(cellType);
178        }
179        return sample;
180    }
181
182    /**
183     * Add a sample[id]-disease into sample.
184     *
185     * @param id SHOULD be positive integer.
186     * @param disease if null ignore operation.
187     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
188     * @return a {@link de.isas.mztab2.model.Sample} object.
189     */
190    public Sample addSampleDisease(Metadata metadata, Integer id, Parameter disease) {
191        if (id <= 0) {
192            throw new IllegalArgumentException("Sample id should be greater than 0!");
193        }
194        Sample sample = sampleMap.get(id);
195        if (disease == null) {
196            return sample;
197        }
198
199        if (sample == null) {
200            sample = new Sample();
201            sample.id(id);
202            sample.addDiseaseItem(disease);
203            sampleMap.put(id, sample);
204            metadata.addSampleItem(sample);
205        } else {
206            sample.addDiseaseItem(disease);
207        }
208        return sample;
209    }
210
211    /**
212     * Add a sample[id]-description into sample.
213     *
214     * @param id SHOULD be positive integer.
215     * @param description if empty ignore operation.
216     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
217     * @return a {@link de.isas.mztab2.model.Sample} object.
218     */
219    public Sample addSampleDescription(Metadata metadata, Integer id, String description) {
220        if (id <= 0) {
221            throw new IllegalArgumentException("Sample id should be greater than 0!");
222        }
223
224        Sample sample = sampleMap.get(id);
225        if (isEmpty(description)) {
226            return sample;
227        }
228
229        if (sample == null) {
230            sample = new Sample();
231            sample.id(id);
232            sample.setDescription(description);
233            sampleMap.put(id, sample);
234            metadata.addSampleItem(sample);
235        } else {
236            sample.setDescription(description);
237        }
238        return sample;
239    }
240
241    /**
242     * Add a sample[id]-custom into sample. Add a custom parameter for sample.
243     *
244     * @param id SHOULD be positive integer.
245     * @param custom if null ignore operation.
246     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
247     * @return a {@link de.isas.mztab2.model.Sample} object.
248     */
249    public Sample addSampleCustom(Metadata metadata, Integer id, Parameter custom) {
250        if (id <= 0) {
251            throw new IllegalArgumentException("Sample id should be greater than 0!");
252        }
253        Sample sample = sampleMap.get(id);
254        if (custom == null) {
255            return sample;
256        }
257
258        if (sample == null) {
259            sample = new Sample();
260            sample.id(id);
261            sample.addCustomItem(custom);
262            sampleMap.put(id, sample);
263            metadata.addSampleItem(sample);
264        } else {
265            sample.addCustomItem(custom);
266        }
267        return sample;
268    }
269
270    /**
271     * Add a sample_processing[id]. A list of parameters describing a sample processing step.
272     * The order of the data_processing items should reflect the order these processing steps
273     * were performed in. If multiple parameters are given for a step these MUST be separated by a "|".
274     *
275     * @param id SHOULD be positive integer.
276     * @param sampleProcessing if null ignore operation.
277     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
278     * @return a {@link de.isas.mztab2.model.SampleProcessing} object.
279     */
280    public SampleProcessing addSampleProcessing(Metadata metadata, Integer id, List<Parameter> sampleProcessing) {
281        if (id <= 0) {
282            throw new IllegalArgumentException("Sample id should be greater than 0!");
283        }
284        if (sampleProcessing == null) {
285            return null;
286        }
287
288//        sampleProcessing.setSplitChar(BAR);
289        SampleProcessing sp = new SampleProcessing();
290        sp.id(id);
291        sp.sampleProcessing(sampleProcessing);
292        metadata.addSampleProcessingItem(sp);
293        sampleProcessingMap.put(id, sp);
294        return sp;
295    }
296
297    /**
298     * Add a processing parameter to sample_processing[id]
299     *
300     * @param id SHOULD be positive integer.
301     * @param param if null ignore operation.
302     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
303     * @return a {@link de.isas.mztab2.model.SampleProcessing} object.
304     */
305    public SampleProcessing addSampleProcessingParameter(Metadata metadata, Integer id, Parameter param) {
306        if (id <= 0) {
307            throw new IllegalArgumentException("Sample processing id should be greater than 0!");
308        }
309        SampleProcessing sampleProcessing = sampleProcessingMap.get(id);
310        if (param == null) {
311            return sampleProcessing;
312        }
313
314        if (sampleProcessing == null) {
315            sampleProcessing = new SampleProcessing();
316            sampleProcessing.id(id);
317            sampleProcessing.addSampleProcessingItem(param);
318            sampleProcessingMap.put(id, sampleProcessing);
319            metadata.addSampleProcessingItem(sampleProcessing);
320        } else {
321            sampleProcessing.addSampleProcessingItem(param);
322        }
323        return sampleProcessing;
324    }
325
326    /**
327     * Add a instrument[id] to metadata.
328     *
329     * @param instrument SHOULD NOT be null.
330     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
331     * @return a {@link de.isas.mztab2.model.Instrument} object.
332     */
333    public Instrument addInstrument(Metadata metadata, Instrument instrument) {
334        if (instrument == null) {
335            throw new IllegalArgumentException("Instrument should not be null");
336        }
337        instrumentMap.put(instrument.getId(), instrument);
338        metadata.addInstrumentItem(instrument);
339        return instrument;
340    }
341
342    /**
343     * Add a parameter for instrument[id]-name
344     *
345     * @param id SHOULD be positive integer.
346     * @param name if null ignore operation.
347     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
348     * @return a {@link de.isas.mztab2.model.Instrument} object.
349     */
350    public Instrument addInstrumentName(Metadata metadata, Integer id, Parameter name) {
351        if (id <= 0) {
352            throw new IllegalArgumentException("Instrument id should be greater than 0!");
353        }
354        Instrument instrument = instrumentMap.get(id);
355        if (name == null) {
356            return instrument;
357        }
358
359        if (instrument == null) {
360            instrument = new Instrument();
361            instrument.id(id);
362            instrument.name(name);
363            instrumentMap.put(id, instrument);
364            metadata.addInstrumentItem(instrument);
365        } else {
366            instrument.name(name);
367        }
368        return instrument;
369    }
370
371    /**
372     * Add a parameter for instrument[id]-source
373     *
374     * @param id SHOULD be positive integer.
375     * @param source if null ignore operation.
376     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
377     * @return a {@link de.isas.mztab2.model.Instrument} object.
378     */
379    public Instrument addInstrumentSource(Metadata metadata, Integer id, Parameter source) {
380        if (id <= 0) {
381            throw new IllegalArgumentException("Instrument id should be greater than 0!");
382        }
383        Instrument instrument = instrumentMap.get(id);
384        if (source == null) {
385            return instrument;
386        }
387
388        if (instrument == null) {
389            instrument = new Instrument();
390            instrument.id(id);
391            instrument.setSource(source);
392            instrumentMap.put(id, instrument);
393            metadata.addInstrumentItem(instrument);
394        } else {
395            instrument.setSource(source);
396        }
397        return instrument;
398    }
399
400    /**
401     * Add a parameter for instrument[id]-analyzer[i]
402     *
403     * @param id SHOULD be positive integer.
404     * @param analyzer if null ignore operation.
405     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
406     * @return a {@link de.isas.mztab2.model.Instrument} object.
407     */
408    public Instrument addInstrumentAnalyzer(Metadata metadata, Integer id, Parameter analyzer) {
409        if (id <= 0) {
410            throw new IllegalArgumentException("Instrument id should be greater than 0!");
411        }
412        Instrument instrument = instrumentMap.get(id);
413        if (analyzer == null) {
414            return instrument;
415        }
416
417        if (instrument == null) {
418            instrument = new Instrument();
419            instrument.id(id);
420            instrument.addAnalyzerItem(analyzer);
421            instrumentMap.put(id, instrument);
422            metadata.addInstrumentItem(instrument);
423        } else {
424            instrument.addAnalyzerItem(analyzer);
425        }
426        return instrument;
427    }
428
429    /**
430     * Add a parameter for instrument[id]-detector
431     *
432     * @param id SHOULD be positive integer.
433     * @param detector if null ignore operation.
434     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
435     * @return a {@link de.isas.mztab2.model.Instrument} object.
436     */
437    public Instrument addInstrumentDetector(Metadata metadata, Integer id, Parameter detector) {
438        if (id <= 0) {
439            throw new IllegalArgumentException("Instrument id should be greater than 0!");
440        }
441
442        Instrument instrument = instrumentMap.get(id);
443        if (detector == null) {
444            return instrument;
445        }
446
447        if (instrument == null) {
448            instrument = new Instrument();
449            instrument.id(id);
450            instrument.setDetector(detector);
451            instrumentMap.put(id, instrument);
452            metadata.addInstrumentItem(instrument);
453        } else {
454            instrument.setDetector(detector);
455        }
456        return instrument;
457    }
458
459    /**
460     * Add a software to metadata, which used to analyze the data and obtain the reported results.
461     *
462     * @param software SHOULD NOT be null
463     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
464     * @return a {@link de.isas.mztab2.model.Software} object.
465     */
466    public Software addSoftware(Metadata metadata, Software software) {
467        if (software == null) {
468            throw new IllegalArgumentException("Software should not be null");
469        }
470
471        softwareMap.put(software.getId(), software);
472        metadata.addSoftwareItem(software);
473        return software;
474    }
475
476    /**
477     * Add a software[id] parameter. The parameter's value SHOULD contain the software's version.
478     * The order (numbering) should reflect the order in which the tools were used.
479     *
480     * @param id SHOULD be positive integer.
481     * @param param if null ignore operation.
482     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
483     * @return a {@link de.isas.mztab2.model.Software} object.
484     */
485    public Software addSoftwareParameter(Metadata metadata, Integer id, Parameter param) {
486        if (id <= 0) {
487            throw new IllegalArgumentException("Software id should be greater than 0!");
488        }
489        Software software = softwareMap.get(id);
490        if (param == null) {
491            return software;
492        }
493
494        if (software == null) {
495            software = new Software();
496            software.id(id);
497            software.setParameter(param);
498            softwareMap.put(id, software);
499            metadata.addSoftwareItem(software);
500        } else {
501            software.setParameter(param);
502        }
503        return software;
504    }
505
506    /**
507     * Add a software[id]-setting. This field MAY occur multiple times for a single software.
508     * The value of this field is deliberately set as a String, since there currently do not
509     * exist cvParameters for every possible setting.
510     *
511     * @param id SHOULD be positive integer.
512     * @param setting if empty ignore operation.
513     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
514     * @return a {@link de.isas.mztab2.model.Software} object.
515     */
516    public Software addSoftwareSetting(Metadata metadata, Integer id, String setting) {
517        if (id <= 0) {
518            throw new IllegalArgumentException("Software id should be greater than 0!");
519        }
520        Software software = softwareMap.get(id);
521        if (isEmpty(setting)) {
522            return software;
523        }
524
525        if (software == null) {
526            software = new Software();
527            software.id(id);
528            software.addSettingItem(setting);
529            softwareMap.put(id, software);
530            metadata.addSoftwareItem(software);
531        } else  {
532            software.addSettingItem(setting);
533        }
534        return software;
535    }
536
537    /**
538     * Add a publiction to metadata. A publication associated with this file. Several publications can be given by
539     * indicating the number in the square brackets after "publication". PubMed ids must be prefixed by "pubmed:",
540     * DOIs by "doi:". Multiple identifiers MUST be separated by "|".
541     *
542     * @param publication SHOULD NOT be null.
543     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
544     * @return a {@link de.isas.mztab2.model.Publication} object.
545     */
546    public Publication addPublication(Metadata metadata, Publication publication) {
547        if (publication == null) {
548            throw new IllegalArgumentException("Publication should not be null");
549        }
550        publicationMap.put(publication.getId(), publication);
551        metadata.addPublicationItem(publication);
552        return publication;
553    }
554
555    /**
556     * Add a publication item to metadata. PubMed ids must be prefixed by "pubmed:", DOIs by "doi:".
557     * Multiple identifiers MUST be separated by "|".
558     *
559     * @param id SHOULD be positive integer.
560     * @param type SHOULD NOT be null.
561     * @param accession SHOULD NOT set empty.
562     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
563     * @return a {@link de.isas.mztab2.model.Publication} object.
564     */
565    public Publication addPublicationItem(Metadata metadata, Integer id, PublicationItem.TypeEnum type, String accession) {
566        if (id <= 0) {
567            throw new IllegalArgumentException("Publication id should be greater than 0!");
568        }
569        if (type == null) {
570            throw new NullPointerException("Publication type should not be null");
571        }
572        if (isEmpty(accession)) {
573            throw new IllegalArgumentException("Publication accession should not be empty.");
574        }
575
576        Publication publication = publicationMap.get(id);
577        if (publication == null) {
578            publication = new Publication();
579            publication.id(id);
580            publication.addPublicationItemsItem(new PublicationItem().type(type).accession(accession));
581            publicationMap.put(id, publication);
582            metadata.addPublicationItem(publication);
583        } else {
584            publication.addPublicationItemsItem(new PublicationItem().type(type).accession(accession));
585        }
586        return publication;
587    }
588
589    /**
590     * Add a couple of publication items into publication[id]. Several publications can be given by
591     * indicating the number in the square brackets after "publication". PubMed ids must be prefixed by "pubmed:",
592     * DOIs by "doi:". Multiple identifiers MUST be separated by "|".
593     *
594     * @param id SHOULD be positive integer.
595     * @param items SHOULD NOT be null.
596     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
597     * @return a {@link de.isas.mztab2.model.Publication} object.
598     */
599    public Publication addPublicationItems(Metadata metadata, Integer id, Collection<PublicationItem> items) {
600        if (id <= 0) {
601            throw new IllegalArgumentException("Publication id should be greater than 0!");
602        }
603        if (items == null) {
604            throw new NullPointerException("Publication items should not be null");
605        }
606
607        Publication publication = publicationMap.get(id);
608        if (publication == null) {
609            publication = new Publication();
610            publication.id(id);
611            publication.setPublicationItems(new ArrayList<>(items));
612            publicationMap.put(id, publication);
613            metadata.addPublicationItem(publication);
614        } else {
615            publication.setPublicationItems(new ArrayList<>(items));
616        }
617        return publication;
618    }
619
620    /**
621     * Add a contact into metadata.
622     *
623     * @param contact SHOULD NOT be null.
624     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
625     * @return a {@link de.isas.mztab2.model.Contact} object.
626     */
627    public Contact addContact(Metadata metadata, Contact contact) {
628        if (contact == null) {
629            throw new IllegalArgumentException("Contact should not be null");
630        }
631
632        contactMap.put(contact.getId(), contact);
633        metadata.addContactItem(contact);
634        return contact;
635    }
636
637    /**
638     * Add contact[id]-name. Several contacts can be given by indicating the number in the square brackets
639     * after "contact". A contact has to be supplied in the format [first name] [initials] [last name] (see example).
640     *
641     * @param id SHOULD be positive integer.
642     * @param name SHOULD NOT set empty.
643     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
644     * @return a {@link de.isas.mztab2.model.Contact} object.
645     */
646    public Contact addContactName(Metadata metadata, Integer id, String name) {
647        if (id <= 0) {
648            throw new IllegalArgumentException("Contact id should be greater than 0!");
649        }
650        if (isEmpty(name)) {
651            throw new IllegalArgumentException("Contact name should not be empty.");
652        }
653
654        Contact contact = contactMap.get(id);
655        if (contact == null) {
656            contact = new Contact();
657            contact.id(id);
658            contact.setName(name);
659            contactMap.put(id, contact);
660            metadata.addContactItem(contact);
661        } else {
662            contact.setName(name);
663        }
664        return contact;
665    }
666
667    /**
668     * Add contact[id]-affiliation.
669     *
670     * @param id SHOULD be positive integer.
671     * @param affiliation SHOULD NOT set empty.
672     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
673     * @return a {@link de.isas.mztab2.model.Contact} object.
674     */
675    public Contact addContactAffiliation(Metadata metadata, Integer id, String affiliation) {
676        if (id <= 0) {
677            throw new IllegalArgumentException("Contact id should be greater than 0!");
678        }
679        if (isEmpty(affiliation)) {
680            throw new IllegalArgumentException("Contact affiliation should not be empty.");
681        }
682
683        Contact contact = contactMap.get(id);
684        if (contact == null) {
685            contact = new Contact();
686            contact.id(id);
687            contact.setAffiliation(affiliation);
688            contactMap.put(id, contact);
689            metadata.addContactItem(contact);
690        } else {
691            contact.setAffiliation(affiliation);
692        }
693        return contact;
694    }
695
696    /**
697     * Add contact[id]-email
698     *
699     * @param id SHOULD be positive integer.
700     * @param email SHOULD NOT set empty.
701     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
702     * @return a {@link de.isas.mztab2.model.Contact} object.
703     */
704    public Contact addContactEmail(Metadata metadata, Integer id, String email) {
705        if (id <= 0) {
706            throw new IllegalArgumentException("Contact id should be greater than 0!");
707        }
708        if (isEmpty(email)) {
709            throw new IllegalArgumentException("Contact email should not be empty.");
710        }
711
712        Contact contact = contactMap.get(id);
713        if (contact == null) {
714            contact = new Contact();
715            contact.id(id);
716            contact.setEmail(email);
717            contactMap.put(id, contact);
718            metadata.addContactItem(contact);
719        } else {
720            contact.setEmail(email);
721        }
722        return contact;
723    }
724
725    /**
726     * Add a ms_run[id] into metadata. An MS run is effectively one run (or set of runs on pre-fractionated samples)
727     * on an MS instrument, and is referenced from assay in different contexts.
728     *
729     * @param msRun SHOULD NOT be null.
730     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
731     * @return a {@link de.isas.mztab2.model.MsRun} object.
732     */
733    public MsRun addMsRun(Metadata metadata, MsRun msRun) {
734        if (msRun == null) {
735            throw new IllegalArgumentException("MsRun should not be null");
736        }
737
738        msRunMap.put(msRun.getId(), msRun);
739        metadata.addMsRunItem(msRun);
740        return msRun;
741    }
742
743    /**
744     * Add ms_run[id]-format into metadata. A parameter specifying the data format of the external MS data file.
745     *
746     * @param id SHOULD be positive integer.
747     * @param format if null ignore operation.
748     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
749     * @return a {@link de.isas.mztab2.model.MsRun} object.
750     */
751    public MsRun addMsRunFormat(Metadata metadata, Integer id, Parameter format) {
752        if (id <= 0) {
753            throw new IllegalArgumentException("ms_run id should be greater than 0!");
754        }
755        MsRun msRun = msRunMap.get(id);
756        if (format == null) {
757            return msRun;
758        }
759
760        if (msRun == null) {
761            msRun = new MsRun();
762            msRun.id(id);
763            msRun.setFormat(format);
764            msRunMap.put(id, msRun);
765            metadata.addMsRunItem(msRun);
766        } else {
767            msRun.setFormat(format);
768        }
769        return msRun;
770    }
771
772    /**
773     * Add ms_run[id]-location into metadata. Location of the external data file. If the actual location
774     * of the MS run is unknown, a "null" MUST be used as a place holder value.
775     *
776     * @param id SHOULD be positive integer.
777     * @param location if null ignore operation.
778     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
779     * @return a {@link de.isas.mztab2.model.MsRun} object.
780     */
781    public MsRun addMsRunLocation(Metadata metadata, Integer id, URI location) {
782        if (id <= 0) {
783            throw new IllegalArgumentException("ms_run id should be greater than 0!");
784        }
785
786        MsRun msRun = msRunMap.get(id);
787        if (msRun == null) {
788            msRun = new MsRun();
789            msRun.id(id);
790            msRun.setLocation(location==null?null:location.toASCIIString());
791            msRunMap.put(id, msRun);
792            metadata.addMsRunItem(msRun);
793        } else {
794            msRun.setLocation(location.toString());
795        }
796        return msRun;
797    }
798    
799    /**
800     * Add ms_run[id]-instrument_ref into metadata. Reference to a commonly used instrument.
801     * 
802     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
803     * @param id SHOULD be positive integer.
804     * @param instrument if null ignore operation.
805     * @return a {@link de.isas.mztab2.model.MsRun} object.
806     */
807    public MsRun addMsRunInstrumentRef(Metadata metadata, Integer id, Instrument instrument) {
808        if (id <= 0) {
809            throw new IllegalArgumentException("ms_run id should be greater than 0!");
810        }
811        
812        if (instrument==null) {
813            throw new IllegalArgumentException("instrument must not be null!");
814        }
815
816        MsRun msRun = msRunMap.get(id);
817        if (msRun == null) {
818            msRun = new MsRun();
819            msRun.id(id);
820            msRun.setInstrumentRef(instrument);
821            msRunMap.put(id, msRun);
822            metadata.addMsRunItem(msRun);
823        } else {
824            msRun.setInstrumentRef(instrument);
825        }
826        return msRun;
827    }
828
829    /**
830     * Add ms_run[id]-id_format into metadata. Parameter specifying the id format used in the external data file.
831     *
832     * @param id SHOULD be positive integer.
833     * @param idFormat if null ignore operation.
834     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
835     * @return a {@link de.isas.mztab2.model.MsRun} object.
836     */
837    public MsRun addMsRunIdFormat(Metadata metadata, Integer id, Parameter idFormat) {
838        if (id <= 0) {
839            throw new IllegalArgumentException("ms_run id should be greater than 0!");
840        }
841        MsRun msRun = msRunMap.get(id);
842        if (idFormat == null) {
843            return msRun;
844        }
845
846        if (msRun == null) {
847            msRun = new MsRun();
848            msRun.id(id);
849            msRun.setIdFormat(idFormat);
850            msRunMap.put(id, msRun);
851            metadata.addMsRunItem(msRun);
852        } else {
853            msRun.setIdFormat(idFormat);
854        }
855        return msRun;
856    }
857
858    /**
859     * Add ms_run[id]-fragmentation_method into metadata. A list of "|" separated parameters describing
860     * all the types of fragmentation used in a given ms run.
861     *
862     * @param id SHOULD be positive integer.
863     * @param fragmentationMethod if null ignore operation.
864     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
865     * @return a {@link de.isas.mztab2.model.MsRun} object.
866     */
867    public MsRun addMsRunFragmentationMethod(Metadata metadata, Integer id, Parameter fragmentationMethod) {
868        if (id <= 0) {
869            throw new IllegalArgumentException("ms_run id should be greater than 0!");
870        }
871        MsRun msRun = msRunMap.get(id);
872        if (fragmentationMethod == null) {
873            return msRun;
874        }
875
876        if (msRun == null) {
877            msRun = new MsRun();
878            msRun.id(id);
879            msRun.addFragmentationMethodItem(fragmentationMethod);
880            msRunMap.put(id, msRun);
881            metadata.addMsRunItem(msRun);
882        } else {
883            msRun.addFragmentationMethodItem(fragmentationMethod);
884        }
885        return msRun;
886    }
887
888    /**
889     * <p>addMsRunHash.</p>
890     *
891     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
892     * @param id a {@link java.lang.Integer} object.
893     * @param hash a {@link java.lang.String} object.
894     * @return a {@link de.isas.mztab2.model.MsRun} object.
895     */
896    public MsRun addMsRunHash(Metadata metadata, Integer id, String hash) {
897        if (id <= 0) {
898            throw new IllegalArgumentException("ms_run id should be greater than 0!");
899        }
900        if (isEmpty(hash)) {
901            throw new IllegalArgumentException("ms_run hash should not be empty.");
902        }
903
904        MsRun msRun = msRunMap.get(id);
905        if (msRun == null) {
906            msRun = new MsRun();
907            msRun.id(id);
908            msRun.setHash(hash);
909            msRunMap.put(id, msRun);
910            metadata.addMsRunItem(msRun);
911        } else {
912            msRun.setHash(hash);
913        }
914        return msRun;
915    }
916
917    /**
918     * <p>addMsRunHashMethod.</p>
919     *
920     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
921     * @param id a {@link java.lang.Integer} object.
922     * @param hashMethod a {@link de.isas.mztab2.model.Parameter} object.
923     * @return a {@link de.isas.mztab2.model.MsRun} object.
924     */
925    public MsRun addMsRunHashMethod(Metadata metadata, Integer id, Parameter hashMethod) {
926        if (id <= 0) {
927            throw new IllegalArgumentException("ms_run id should be greater than 0!");
928        }
929        MsRun msRun = msRunMap.get(id);
930        if (hashMethod == null) {
931            return msRun;
932        }
933
934        if (msRun == null) {
935            msRun = new MsRun();
936            msRun.id(id);
937            msRun.setHashMethod(hashMethod);
938            msRunMap.put(id, msRun);
939            metadata.addMsRunItem(msRun);
940        } else {
941            msRun.setHashMethod(hashMethod);
942        }
943        return msRun;
944    }
945    
946    /**
947     * <p>addMsRunScanPolarity.</p>
948     *
949     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
950     * @param id a {@link java.lang.Integer} object.
951     * @param scanPolarity a {@link de.isas.mztab2.model.Parameter} object.
952     * @return a {@link de.isas.mztab2.model.MsRun} object.
953     */
954    public MsRun addMsRunScanPolarity(Metadata metadata, Integer id, Parameter scanPolarity) {
955        if (id <= 0) {
956            throw new IllegalArgumentException("ms_run id should be greater than 0!");
957        }
958        MsRun msRun = msRunMap.get(id);
959        if (scanPolarity == null) {
960            return msRun;
961        }
962
963        if (msRun == null) {
964            msRun = new MsRun();
965            msRun.id(id);
966            msRun.addScanPolarityItem(scanPolarity);
967            msRunMap.put(id, msRun);
968            metadata.addMsRunItem(msRun);
969        } else {
970            msRun.addScanPolarityItem(scanPolarity);
971        }
972        return msRun;
973    }
974
975    /**
976     * Add a assay into metadata. The application of a measurement about the sample (in this case through MS) -
977     * producing values about small molecules, peptides or proteins. One assay is typically mapped to one MS run
978     * in the case of label-free MS analysis or multiple assays are mapped to one MS run for multiplexed techniques,
979     * along with a description of the label or tag applied.
980     *
981     * @param assay SHOULD NOT be null.
982     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
983     * @return a {@link de.isas.mztab2.model.Assay} object.
984     */
985    public Assay addAssay(Metadata metadata, Assay assay) {
986        if (assay == null) {
987            throw new IllegalArgumentException("Assay should not be null");
988        }
989
990        assayMap.put(assay.getId(), assay);
991        metadata.addAssayItem(assay);
992        return assay;
993    }
994    
995    /**
996     * Add a assay[id]-custom[i] into metadata. The application of a measurement about the sample (in this case through MS) -
997     * producing values about small molecules, peptides or proteins. One assay is typically mapped to one MS run
998     * in the case of label-free MS analysis or multiple assays are mapped to one MS run for multiplexed techniques,
999     * along with a description of the label or tag applied.
1000     *
1001     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1002     * @param id SHOULD NOT be null.
1003     * @param param the parameter.
1004     * @return a {@link de.isas.mztab2.model.Assay} object.
1005     */
1006    public Assay addAssayCustom(Metadata metadata, Integer id, Parameter param) {
1007        if (id <= 0) {
1008            throw new IllegalArgumentException("assay id should be greater than 0!");
1009        }
1010        
1011        Assay assay = assayMap.get(id);
1012        if (assay == null) {
1013            assay = new Assay();
1014            assay.id(id);
1015            assay.addCustomItem(param);
1016            assayMap.put(id, assay);
1017            metadata.addAssayItem(assay);
1018        } else {
1019            assay.addCustomItem(param);
1020        }
1021        return assay;
1022    }
1023    
1024        /**
1025     * Add a assay[id]-external_uri into metadata. The application of a measurement about the sample (in this case through MS) -
1026     * producing values about small molecules, peptides or proteins. One assay is typically mapped to one MS run
1027     * in the case of label-free MS analysis or multiple assays are mapped to one MS run for multiplexed techniques,
1028     * along with a description of the label or tag applied.
1029     *
1030     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1031     * @param id the id of the assay element.
1032     * @param location SHOULD NOT be null.
1033     * @return a {@link de.isas.mztab2.model.Assay} object.
1034     */
1035    public Assay addAssayExternalUri(Metadata metadata, Integer id, URI location) {
1036        if (id <= 0) {
1037            throw new IllegalArgumentException("assay id should be greater than 0!");
1038        }
1039
1040        Assay assay = assayMap.get(id);
1041        if (assay == null) {
1042            assay = new Assay();
1043            assay.id(id);
1044            assay.setExternalUri(location==null?null:location.toASCIIString());
1045            assayMap.put(id, assay);
1046            metadata.addAssayItem(assay);
1047        } else {
1048            assay.setExternalUri(location.toString());
1049        }
1050        return assay;
1051    }
1052
1053    /**
1054     * Add assay[id]-sample_ref into metadata. An association from a given assay to the sample analysed.
1055     *
1056     * @param id SHOULD be positive integer.
1057     * @param sample SHOULD NOT be null, and SHOULD be defined in metadata first.
1058     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1059     * @return a {@link de.isas.mztab2.model.Assay} object.
1060     */
1061    public Assay addAssaySample(Metadata metadata, Integer id, Sample sample) {
1062        if (id <= 0) {
1063            throw new IllegalArgumentException("assay id should be greater than 0!");
1064        }
1065        if (sample == null) {
1066            throw new NullPointerException("assay sample_ref should not be null.");
1067        }
1068        if (! sampleMap.containsValue(sample)) {
1069            throw new IllegalArgumentException("Sample not defined in metadata.");
1070        }
1071
1072        Assay assay = assayMap.get(id);
1073        if (assay == null) {
1074            assay = new Assay();
1075            assay.id(id);
1076            assay.setSampleRef(sample);
1077            assayMap.put(id, assay);
1078            metadata.addAssayItem(assay);
1079        } else {
1080            assay.setSampleRef(sample);
1081        }
1082        return assay;
1083    }
1084
1085    /**
1086     * Add assay[id]-ms_run_ref into metadata. An association from a given assay to the source MS run.
1087     *
1088     * @param id SHOULD be positive integer.
1089     * @param msRun SHOULD NOT be null, and SHOULD be defined in metadata first.
1090     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1091     * @return a {@link de.isas.mztab2.model.Assay} object.
1092     */
1093    public Assay addAssayMsRun(Metadata metadata, Integer id, MsRun msRun) {
1094        if (id <= 0) {
1095            throw new IllegalArgumentException("assay id should be greater than 0!");
1096        }
1097        if (msRun == null) {
1098            throw new NullPointerException("assay ms_run_ref should not be null.");
1099        }
1100        if (! msRunMap.containsValue(msRun)) {
1101            throw new IllegalArgumentException("ms_run should be defined in metadata first.");
1102        }
1103
1104        Assay assay = assayMap.get(id);
1105        if (assay == null) {
1106            assay = new Assay();
1107            assay.id(id);
1108            assay.addMsRunRefItem(msRun);
1109            assayMap.put(id, assay);
1110            metadata.addAssayItem(assay);
1111        } else {
1112            assay.addMsRunRefItem(msRun);
1113        }
1114        return assay;
1115    }
1116
1117    /**
1118     * Add a study variable into metadata. The variables about which the final results of a study are reported, which
1119     * may have been derived following averaging across a group of replicate measurements (assays). In files where assays
1120     * are reported, study variables have references to assays. The same concept has been defined by others as "experimental factor".
1121     *
1122     * @param studyVariable SHOULD NOT be null.
1123     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1124     * @return a {@link de.isas.mztab2.model.StudyVariable} object.
1125     */
1126    public StudyVariable addStudyVariable(Metadata metadata, StudyVariable studyVariable) {
1127        if (studyVariable == null) {
1128            throw new IllegalArgumentException("StudyVariable should not be null");
1129        }
1130        studyVariableMap.put(studyVariable.getId(), studyVariable);
1131        metadata.addStudyVariableItem(studyVariable);
1132        return studyVariable;
1133    }
1134
1135    /**
1136     * Add a study_variable[id]-assay_refs. Comma-separated references to the IDs of assays grouped in the study variable.
1137     *
1138     * @param id SHOULD be positive integer.
1139     * @param assay SHOULD NOT be null, and should be defined in metadata first.
1140     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1141     * @return a {@link de.isas.mztab2.model.StudyVariable} object.
1142     */
1143    public StudyVariable addStudyVariableAssay(Metadata metadata, Integer id, Assay assay) {
1144        if (id <= 0) {
1145            throw new IllegalArgumentException("study variable id should be greater than 0!");
1146        }
1147        if (assay == null) {
1148            throw new NullPointerException("study_variable[n]-assay_ref should not be null.");
1149        }
1150        if (! assayMap.containsValue(assay)) {
1151            throw new IllegalArgumentException("assay should be defined in metadata first");
1152        }
1153
1154        StudyVariable studyVariable = studyVariableMap.get(id);
1155        if (studyVariable == null) {
1156            studyVariable = new StudyVariable();
1157            studyVariable.id(id);
1158            studyVariable.addAssayRefsItem(assay);
1159            studyVariableMap.put(id, studyVariable);
1160            metadata.addStudyVariableItem(studyVariable);
1161        } else {
1162            studyVariable.addAssayRefsItem(assay);
1163        }
1164        return studyVariable;
1165    }
1166
1167    /**
1168     * Add a study_variable[id]-description. A textual description of the study variable.
1169     *
1170     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1171     * @param id SHOULD be positive integer.
1172     * @param description if empty ignore operation.
1173     * @return a {@link de.isas.mztab2.model.StudyVariable} object.
1174     */
1175    public StudyVariable addStudyVariableDescription(Metadata metadata, Integer id, String description) {
1176        if (id <= 0) {
1177            throw new IllegalArgumentException("study variable id should be greater than 0!");
1178        }
1179        StudyVariable studyVariable = studyVariableMap.get(id);
1180        if (isEmpty(description)) {
1181            return studyVariable;
1182        }
1183
1184        if (studyVariable == null) {
1185            studyVariable = new StudyVariable();
1186            studyVariable.id(id);
1187            metadata.addStudyVariableItem(studyVariable);
1188        }
1189
1190        studyVariable.setDescription(description);
1191        studyVariableMap.put(id, studyVariable);
1192        return studyVariable;
1193    }
1194    
1195    /**
1196     * Add a study_variable[id]-factor. A Parameter further refining what is known about the study design.
1197     * 
1198     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1199     * @param id id SHOULD be positive integer.
1200     * @param checkParameter the study variable factor Parameter to add.
1201     * @return a {@link de.isas.mztab2.model.StudyVariable} object.
1202     */
1203    public StudyVariable addStudyVariableFactors(Metadata metadata, Integer id, Parameter checkParameter) {
1204        if (id <= 0) {
1205            throw new IllegalArgumentException("study variable id should be greater than 0!");
1206        }
1207        StudyVariable studyVariable = studyVariableMap.get(id);
1208        if (checkParameter == null) {
1209            return studyVariable;
1210        }
1211
1212        if (studyVariable == null) {
1213            studyVariable = new StudyVariable();
1214            studyVariable.id(id);
1215            metadata.addStudyVariableItem(studyVariable);
1216        }
1217
1218        studyVariable.addFactorsItem(checkParameter);
1219        studyVariableMap.put(id, studyVariable);
1220        return studyVariable;
1221    }
1222    
1223    /**
1224     * Add a study_variable[id]-variation_function. This is a Parameter detailing how the
1225     * reported study variable abundances have been calculated.
1226     *
1227     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1228     * @param id SHOULD be positive integer.
1229     * @param checkParameter the parameter.
1230     * @return a {@link de.isas.mztab2.model.StudyVariable} object.
1231     */
1232    public StudyVariable addStudyVariableVariationFunction(Metadata metadata, Integer id,
1233        Parameter checkParameter) {
1234        if (id <= 0) {
1235            throw new IllegalArgumentException("study variable id should be greater than 0!");
1236        }
1237        StudyVariable studyVariable = studyVariableMap.get(id);
1238        if (checkParameter == null) {
1239            return studyVariable;
1240        }
1241        if (studyVariable == null) {
1242            studyVariable = new StudyVariable();
1243            studyVariable.id(id);
1244            studyVariable.setVariationFunction(checkParameter);
1245            studyVariableMap.put(id, studyVariable);
1246            metadata.addStudyVariableItem(studyVariable);
1247        } else {
1248            studyVariable.setVariationFunction(checkParameter);
1249        }
1250        return studyVariable;
1251    }
1252    
1253    /**
1254     * Add a study_variable[id]-average_function. This is a Parameter detailing how the
1255     * reported study variable abundances have been calculated.
1256     *
1257     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1258     * @param id SHOULD be positive integer.
1259     * @param checkParameter the parameter.
1260     * @return a {@link de.isas.mztab2.model.StudyVariable} object.
1261     */
1262    public StudyVariable addStudyVariableAverageFunction(Metadata metadata, Integer id,
1263        Parameter checkParameter) {
1264        if (id <= 0) {
1265            throw new IllegalArgumentException("study variable id should be greater than 0!");
1266        }
1267        StudyVariable studyVariable = studyVariableMap.get(id);
1268        if (checkParameter == null) {
1269            return studyVariable;
1270        }
1271        if (studyVariable == null) {
1272            studyVariable = new StudyVariable();
1273            studyVariable.id(id);
1274            studyVariable.setAverageFunction(checkParameter);
1275            studyVariableMap.put(id, studyVariable);
1276            metadata.addStudyVariableItem(studyVariable);
1277        } else {
1278            studyVariable.setAverageFunction(checkParameter);
1279        }
1280        return studyVariable;
1281    }
1282
1283    /**
1284     * Add a controlled vocabularies/ontologies into metadata. Define the controlled vocabularies/ontologies
1285     * used in the mzTab file.
1286     *
1287     * @param cv SHOULD NOT be null.
1288     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1289     * @return a {@link de.isas.mztab2.model.CV} object.
1290     */
1291    public CV addCV(Metadata metadata, CV cv) {
1292        if (cv == null) {
1293            throw new NullPointerException("Controlled vocabularies/ontologies can not set null!");
1294        }
1295
1296        cvMap.put(cv.getId(), cv);
1297        metadata.addCvItem(cv);
1298        return cv;
1299    }
1300
1301    /**
1302     * Add a cv[id]-label. A string describing the labels of the controlled vocabularies/ontologies used in the mzTab file
1303     *
1304     * @param id SHOULD be positive integer.
1305     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1306     * @param label a {@link java.lang.String} object.
1307     * @return a {@link de.isas.mztab2.model.CV} object.
1308     */
1309    public CV addCVLabel(Metadata metadata, Integer id, String label) {
1310        if (id <= 0) {
1311            throw new IllegalArgumentException("controlled vocabularies id should be greater than 0!");
1312        }
1313
1314        CV cv = cvMap.get(id);
1315        if (cv == null) {
1316            cv = new CV();
1317            cv.id(id);
1318            metadata.addCvItem(cv);
1319        }
1320
1321        cv.setLabel(label);
1322        return cvMap.put(id, cv);
1323    }
1324
1325    /**
1326     * Add a cv[id]-full_name. A string describing the full names of the controlled vocabularies/ontologies used in
1327     * the mzTab file
1328     *
1329     * @param id SHOULD be positive integer.
1330     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1331     * @param fullName a {@link java.lang.String} object.
1332     * @return a {@link de.isas.mztab2.model.CV} object.
1333     */
1334    public CV addCVFullName(Metadata metadata, Integer id, String fullName) {
1335        if (id <= 0) {
1336            throw new IllegalArgumentException("controlled vocabularies id should be greater than 0!");
1337        }
1338
1339        CV cv = cvMap.get(id);
1340        if (cv == null) {
1341            cv = new CV();
1342            cv.id(id);
1343            metadata.addCvItem(cv);
1344        }
1345
1346        cv.setFullName(fullName);
1347        return cvMap.put(id, cv);
1348    }
1349
1350    /**
1351     * Add a cv[id]-version. A string describing the version of the controlled vocabularies/ontologies used in
1352     * the mzTab file
1353     *
1354     * @param id SHOULD be positive integer.
1355     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1356     * @param version a {@link java.lang.String} object.
1357     * @return a {@link de.isas.mztab2.model.CV} object.
1358     */
1359    public CV addCVVersion(Metadata metadata, Integer id, String version) {
1360        if (id <= 0) {
1361            throw new IllegalArgumentException("controlled vocabularies id should be greater than 0!");
1362        }
1363
1364        CV cv = cvMap.get(id);
1365        if (cv == null) {
1366            cv = new CV();
1367            cv.id(id);
1368            metadata.addCvItem(cv);
1369        }
1370
1371        cv.setVersion(version);
1372        return cvMap.put(id, cv);
1373    }
1374
1375    /**
1376     * Add a cv[id]-uri. A string containing the URIs of the controlled vocabularies/ontologies used in the
1377     * mzTab file
1378     *
1379     * @param id SHOULD be positive integer.
1380     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1381     * @param uri a {@link java.lang.String} object.
1382     * @return a {@link de.isas.mztab2.model.CV} object.
1383     */
1384    public CV addCVURI(Metadata metadata, Integer id, String uri) {
1385        if (id <= 0) {
1386            throw new IllegalArgumentException("controlled vocabularies id should be greater than 0!");
1387        }
1388
1389        CV cv = cvMap.get(id);
1390        if (cv == null) {
1391            cv = new CV();
1392            cv.id(id);
1393            metadata.addCvItem(cv);
1394        }
1395
1396        cv.setUri(uri);
1397        return cvMap.put(id, cv);
1398    }
1399
1400    /**
1401     * Defines the unit for the data reported in a column of the small molecule section. Defines the used unit for a column
1402     * in the small molecule section. The format of the value has to be {column name}={Parameter defining the unit}
1403     * This field MUST NOT be used to define a unit for quantification columns. The unit used for small molecule quantification
1404     * values MUST be set in small_molecule-quantification_unit.
1405     *
1406     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1407     * @param columnName SHOULD NOT be null
1408     * @param param SHOULD NOT be null
1409     */
1410    public void addSmallMoleculeColUnit(Metadata metadata, String columnName, Parameter param) {
1411        ColumnParameterMapping cpm = new ColumnParameterMapping();
1412        cpm.columnName(columnName).param(param);
1413        this.smallMoleculeColUnitList.add(cpm);
1414        metadata.addColunitSmallMoleculeItem(cpm);
1415    }
1416    
1417    /**
1418     * Defines the unit for the data reported in a column of the small molecule section. Defines the used unit for a column
1419     * in the small molecule section. The format of the value has to be {column name}={Parameter defining the unit}
1420     * This field MUST NOT be used to define a unit for quantification columns. The unit used for small molecule quantification
1421     * values MUST be set in small_molecule-quantification_unit.
1422     *
1423     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1424     * @param columnName SHOULD NOT be null
1425     * @param param SHOULD NOT be null
1426     */
1427    public void addSmallMoleculeFeatureColUnit(Metadata metadata, String columnName, Parameter param) {
1428        ColumnParameterMapping cpm = new ColumnParameterMapping();
1429        cpm.columnName(columnName).param(param);
1430        this.smallMoleculeFeatureColUnitList.add(cpm);
1431        metadata.addColunitSmallMoleculeFeatureItem(cpm);
1432    }
1433    
1434    /**
1435     * Defines the unit for the data reported in a column of the small molecule section. Defines the used unit for a column
1436     * in the small molecule section. The format of the value has to be {column name}={Parameter defining the unit}
1437     * This field MUST NOT be used to define a unit for quantification columns. The unit used for small molecule quantification
1438     * values MUST be set in small_molecule-quantification_unit.
1439     *
1440     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1441     * @param columnName SHOULD NOT be null
1442     * @param param SHOULD NOT be null
1443     */
1444    public void addSmallMoleculeEvidenceColUnit(Metadata metadata, String columnName, Parameter param) {
1445        ColumnParameterMapping cpm = new ColumnParameterMapping();
1446        cpm.columnName(columnName).param(param);
1447        this.smallMoleculeEvidenceColUnitList.add(cpm);
1448        metadata.addColunitSmallMoleculeEvidenceItem(cpm);
1449    }
1450
1451    /**
1452     *  Defines a method to access the colUnit to help in the transformation from columnName String -&gt; to columnName MZTabColumn
1453     *
1454     * @return a {@link java.util.Map} object.
1455     */
1456    public Map<String, String> getColUnitMap() {
1457        return colUnitMap;
1458    }
1459
1460    /**
1461     * Add a confidence measure id parameter.
1462     * @param id SHOULD NOT be null
1463     * @param parameter SHOULD NOT be null
1464     */
1465    void addIdConfidenceMeasure(Metadata metadata, Integer id, Parameter parameter) {
1466        if(parameter.getId()==null) {
1467            parameter.setId(id);
1468        }
1469        this.idConfidenceMeasureMap.put(id, parameter);
1470        metadata.addIdConfidenceMeasureItem(parameter);
1471    }
1472
1473    /**
1474     * Add a custom item parameter.
1475     * @param metadata
1476     * @param id
1477     * @param custom 
1478     * @return
1479     */
1480    Parameter addCustomItem(Metadata metadata, Integer id, Parameter custom) {
1481        if (custom == null) {
1482            return null;
1483        }
1484        if(custom.getId()==null) {
1485            custom.setId(id);
1486        }
1487        this.customItemMap.put(id, custom);
1488        metadata.addCustomItem(custom);
1489        return custom;
1490    }
1491    
1492    /**
1493     * Add a derivatization agent parameter.
1494     * @param metadata
1495     * @param id
1496     * @param derivatizationAgent
1497     * @return 
1498     */
1499    Parameter addDerivatizationAgentItem(Metadata metadata, Integer id, Parameter derivatizationAgent) {
1500        if (derivatizationAgent == null) {
1501            return null;
1502        }
1503        if(derivatizationAgent.getId()==null) {
1504            derivatizationAgent.setId(id);
1505        }
1506        this.derivatizationItemMap.put(id, derivatizationAgent);
1507        metadata.addDerivatizationAgentItem(derivatizationAgent);
1508        return derivatizationAgent;
1509    }
1510
1511    /**
1512     * <p>addDatabase.</p>
1513     *
1514     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1515     * @param database a {@link de.isas.mztab2.model.Database} object.
1516     * @return a {@link de.isas.mztab2.model.Database} object.
1517     */
1518    public Database addDatabase(Metadata metadata, Database database) {
1519        if (database == null) {
1520            throw new IllegalArgumentException("Database should not be null");
1521        }
1522        databaseMap.put(database.getId(), database);
1523        metadata.addDatabaseItem(database);
1524        return database;
1525    }
1526
1527    /**
1528     * <p>addDatabasePrefix.</p>
1529     *
1530     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1531     * @param id a {@link java.lang.Integer} object.
1532     * @param valueLabel a {@link java.lang.String} object.
1533     * @return a {@link de.isas.mztab2.model.Database} object.
1534     */
1535    public Database addDatabasePrefix(Metadata metadata, Integer id, String valueLabel) {
1536        if (id <= 0) {
1537            throw new IllegalArgumentException("database id should be greater than 0!");
1538        }
1539
1540        Database database = databaseMap.get(id);
1541        if (database == null) {
1542            database = new Database();
1543            database.id(id);
1544            database.setPrefix(valueLabel);
1545            databaseMap.put(id, database);
1546            metadata.addDatabaseItem(database);
1547        } else {
1548            database.setPrefix(valueLabel);
1549        }
1550        return database;
1551    }
1552
1553    /**
1554     * <p>addDatabaseVersion.</p>
1555     *
1556     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1557     * @param id a {@link java.lang.Integer} object.
1558     * @param version a {@link java.lang.String} object.
1559     * @return a {@link de.isas.mztab2.model.Database} object.
1560     */
1561    public Database addDatabaseVersion(Metadata metadata, Integer id, String version) {
1562        if (id <= 0) {
1563            throw new IllegalArgumentException("database id should be greater than 0!");
1564        }
1565
1566        Database database = databaseMap.get(id);
1567        if (database == null) {
1568            database = new Database();
1569            database.id(id);
1570            database.setVersion(version);
1571            databaseMap.put(id, database);
1572            metadata.addDatabaseItem(database);
1573        } else {
1574            database.setVersion(version);
1575        }
1576        return database;
1577    }
1578
1579    /**
1580     * <p>addDatabaseUri.</p>
1581     *
1582     * @param metadata a {@link de.isas.mztab2.model.Metadata} object.
1583     * @param id a {@link java.lang.Integer} object.
1584     * @param checkURI a {@link java.net.URI} object.
1585     * @return a {@link de.isas.mztab2.model.Database} object.
1586     */
1587    public Database addDatabaseUri(Metadata metadata, Integer id, URI checkURI) {
1588        if (id <= 0) {
1589            throw new IllegalArgumentException("database id should be greater than 0!");
1590        }
1591
1592        Database database = databaseMap.get(id);
1593        if (database == null) {
1594            database = new Database();
1595            database.id(id);
1596            database.setUri(checkURI==null?null:checkURI.toString());
1597            databaseMap.put(id, database);
1598            metadata.addDatabaseItem(database);
1599        } else {
1600            database.setUri(checkURI==null?null:checkURI.toString());
1601        }
1602        return database;
1603    }
1604
1605}