From e4c192032d85d07ee806c66611a5b2d83f1397da Mon Sep 17 00:00:00 2001 From: vagisha Date: Mon, 18 Nov 2024 18:33:23 -0800 Subject: [PATCH] Set qcTraceName on ChromatogramGroupId (#982) * Set qcTraceName on ChromatogramGroupId. * Fix error importing Skyline documents with Spectrum Filters (#921) "Spectrum Filter" gets parsed from the Skyline document light:heavy ratios are not calculated for precursors that have a spectrum filter The spectrum filter is used when deciding which chromatogram to read from the .skyd file for a particular precursor. # Conflicts: # src/org/labkey/targetedms/parser/ChromatogramGroupId.java * Added constructor for ChromatogramGroupId that takes both qcTraceName and spectrumFilter. --------- Co-authored-by: nickshulman --- .../postgresql/targetedms-24.004-24.005.sql | 2 + .../sqlserver/targetedms-24.004-24.005.sql | 1 + resources/schemas/targetedms.xml | 1 + .../labkey/targetedms/SkylineDocImporter.java | 2 + .../labkey/targetedms/TargetedMSModule.java | 6 +- .../parser/ChromatogramGroupId.java | 29 +- .../targetedms/parser/GeneralPrecursor.java | 11 + .../parser/PeakAreaRatioCalculator.java | 7 + .../parser/SkylineBinaryParser.java | 2 +- .../parser/SkylineDocumentParser.java | 49 ++- .../targetedms/parser/SpectrumFilter.java | 295 ++++++++++++++++++ .../SpectrumFilterTest.sky.zip | Bin 0 -> 42948 bytes .../TargetedMSDocumentFormatsTest.java | 8 + 13 files changed, 394 insertions(+), 19 deletions(-) create mode 100644 resources/schemas/dbscripts/postgresql/targetedms-24.004-24.005.sql create mode 100644 resources/schemas/dbscripts/sqlserver/targetedms-24.004-24.005.sql create mode 100644 src/org/labkey/targetedms/parser/SpectrumFilter.java create mode 100644 test/sampledata/TargetedMS/DocumentFormats/SpectrumFilterTest.sky.zip diff --git a/resources/schemas/dbscripts/postgresql/targetedms-24.004-24.005.sql b/resources/schemas/dbscripts/postgresql/targetedms-24.004-24.005.sql new file mode 100644 index 000000000..a67743eb7 --- /dev/null +++ b/resources/schemas/dbscripts/postgresql/targetedms-24.004-24.005.sql @@ -0,0 +1,2 @@ +ALTER TABLE targetedms.GeneralPrecursor + ADD COLUMN SpectrumFilter BYTEA; \ No newline at end of file diff --git a/resources/schemas/dbscripts/sqlserver/targetedms-24.004-24.005.sql b/resources/schemas/dbscripts/sqlserver/targetedms-24.004-24.005.sql new file mode 100644 index 000000000..8cfa72753 --- /dev/null +++ b/resources/schemas/dbscripts/sqlserver/targetedms-24.004-24.005.sql @@ -0,0 +1 @@ +ALTER TABLE targetedms.GeneralPrecursor ADD SpectrumFilter VARBINARY(MAX); \ No newline at end of file diff --git a/resources/schemas/targetedms.xml b/resources/schemas/targetedms.xml index 8b74a6b92..283a8c550 100644 --- a/resources/schemas/targetedms.xml +++ b/resources/schemas/targetedms.xml @@ -438,6 +438,7 @@ DateTime + diff --git a/src/org/labkey/targetedms/SkylineDocImporter.java b/src/org/labkey/targetedms/SkylineDocImporter.java index bd4ba0ab3..b3388a101 100644 --- a/src/org/labkey/targetedms/SkylineDocImporter.java +++ b/src/org/labkey/targetedms/SkylineDocImporter.java @@ -1521,6 +1521,8 @@ else if (generalMolecule instanceof Molecule molecule) { if (precursor.getIsotopeLabelId() != numLabelId) continue; + if (precursor.getSpectrumFilter() != null) + continue; PrecursorAreaRatio pRatio = areaRatioCalculator.getPrecursorAreaRatio(sampleFile.getId(), precursor, diff --git a/src/org/labkey/targetedms/TargetedMSModule.java b/src/org/labkey/targetedms/TargetedMSModule.java index 94344db8c..69bdeca96 100644 --- a/src/org/labkey/targetedms/TargetedMSModule.java +++ b/src/org/labkey/targetedms/TargetedMSModule.java @@ -69,6 +69,7 @@ import org.labkey.targetedms.folderImport.QCFolderWriterFactory; import org.labkey.targetedms.parser.Protein; import org.labkey.targetedms.parser.SampleFile; +import org.labkey.targetedms.parser.SpectrumFilter; import org.labkey.targetedms.parser.skyaudit.SkylineAuditLogParser; import org.labkey.targetedms.passport.PassportController; import org.labkey.targetedms.pipeline.TargetedMSPipelineProvider; @@ -222,7 +223,7 @@ public String getName() @Override public Double getSchemaVersion() { - return 24.004; + return 24.005; } @Override @@ -668,7 +669,8 @@ public Set getUnitTests() TargetedMSController.TestCase.class, PrecursorManager.TestCase.class, CrossLinkedPeptideInfo.TestCase.class, - Protein.TestCase.class + Protein.TestCase.class, + SpectrumFilter.TestCase.class ); } diff --git a/src/org/labkey/targetedms/parser/ChromatogramGroupId.java b/src/org/labkey/targetedms/parser/ChromatogramGroupId.java index cf1847ce3..74a03a513 100644 --- a/src/org/labkey/targetedms/parser/ChromatogramGroupId.java +++ b/src/org/labkey/targetedms/parser/ChromatogramGroupId.java @@ -15,22 +15,32 @@ */ package org.labkey.targetedms.parser; +import org.apache.commons.lang3.StringUtils; import org.labkey.targetedms.parser.proto.ChromatogramGroupDataOuterClass; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; public class ChromatogramGroupId { private Target _target; private String _qcTraceName; + private SpectrumFilter _spectrumFilter; private ChromatogramGroupId() { } - public ChromatogramGroupId(Target target) + public ChromatogramGroupId(Target target, String qcTraceName, SpectrumFilter spectrumFilter) { _target = target; + _qcTraceName = StringUtils.isEmpty(qcTraceName) ? null : qcTraceName; + _spectrumFilter = spectrumFilter; + } + + public ChromatogramGroupId(Target target, SpectrumFilter spectrumFilter) + { + this(target, null, spectrumFilter); } public Target getTarget() @@ -43,6 +53,11 @@ public String getQcTraceName() return _qcTraceName; } + public SpectrumFilter getSpectrumFilter() + { + return _spectrumFilter; + } + public static ChromatogramGroupId forQcTraceName(String qcTraceName) { ChromatogramGroupId chromatogramGroupId = new ChromatogramGroupId(); @@ -55,14 +70,24 @@ public static List fromProtos(ChromatogramGroupDataOuterCla List targets = new ArrayList<>(); // Make one-based lookups easy targets.add(null); + + List filterClauses = new ArrayList<>(); + for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.Target target : proto.getTargetsList()) { targets.add(new Target(target)); } + for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter spectrumFilter : proto.getFiltersList()) + { + filterClauses.add(SpectrumFilter.FilterClause.fromProtocolMessage(spectrumFilter)); + } List list = new ArrayList<>(); for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.ChromatogramGroupId chromatogramGroupId : proto.getChromatogramGroupIdsList()) { - list.add(new ChromatogramGroupId(targets.get(chromatogramGroupId.getTargetIndex()))); + SpectrumFilter spectrumFilter = SpectrumFilter.fromFilterClauses( + chromatogramGroupId.getFilterIndexesList().stream() + .map(filterClauses::get).collect(Collectors.toList())).orElse(null); + list.add(new ChromatogramGroupId(targets.get(chromatogramGroupId.getTargetIndex()), chromatogramGroupId.getQcTraceName(), spectrumFilter)); } return list; } diff --git a/src/org/labkey/targetedms/parser/GeneralPrecursor.java b/src/org/labkey/targetedms/parser/GeneralPrecursor.java index 49a527de1..b40f843ef 100644 --- a/src/org/labkey/targetedms/parser/GeneralPrecursor.java +++ b/src/org/labkey/targetedms/parser/GeneralPrecursor.java @@ -38,6 +38,7 @@ public class GeneralPrecursor extends private Double _explicitCcsSqa; private Double _explicitCompensationVoltage; private Double _precursorConcentration; + private byte[] _spectrumFilter; public long getGeneralMoleculeId() { @@ -213,4 +214,14 @@ public void setPrecursorConcentration(Double precursorConcentration) { _precursorConcentration = precursorConcentration; } + + public byte[] getSpectrumFilter() + { + return _spectrumFilter; + } + + public void setSpectrumFilter(byte[] spectrumFilter) + { + _spectrumFilter = spectrumFilter; + } } diff --git a/src/org/labkey/targetedms/parser/PeakAreaRatioCalculator.java b/src/org/labkey/targetedms/parser/PeakAreaRatioCalculator.java index d0675e753..19d245f8e 100644 --- a/src/org/labkey/targetedms/parser/PeakAreaRatioCalculator.java +++ b/src/org/labkey/targetedms/parser/PeakAreaRatioCalculator.java @@ -58,6 +58,13 @@ public void init(Map skylineIdSamp for(Precursor precursor: _peptide.getPrecursorList()) { + if (precursor.getSpectrumFilter() != null) + { + // Ideally, ratios would be calculated separately between Precursors with the same Spectrum Filter. + // However, we only store one ratio in the database for each Peptide, so we just skip precursors + // which have a Spectrum Filter. + continue; + } for(PrecursorChromInfo precursorChromInfo : precursor.getChromInfoList()) { if(precursorChromInfo.isOptimizationPeak()) diff --git a/src/org/labkey/targetedms/parser/SkylineBinaryParser.java b/src/org/labkey/targetedms/parser/SkylineBinaryParser.java index 60c72bb05..e72f0df66 100644 --- a/src/org/labkey/targetedms/parser/SkylineBinaryParser.java +++ b/src/org/labkey/targetedms/parser/SkylineBinaryParser.java @@ -383,7 +383,7 @@ public ChromatogramGroupId getTextId(ChromGroupHeaderInfo chromGroupHeaderInfo) { return null; } - return new ChromatogramGroupId(target); + return new ChromatogramGroupId(target, null); } public String getFilePath(ChromGroupHeaderInfo chromGroupHeaderInfo) { diff --git a/src/org/labkey/targetedms/parser/SkylineDocumentParser.java b/src/org/labkey/targetedms/parser/SkylineDocumentParser.java index a9ad813f7..d798d635d 100644 --- a/src/org/labkey/targetedms/parser/SkylineDocumentParser.java +++ b/src/org/labkey/targetedms/parser/SkylineDocumentParser.java @@ -17,6 +17,7 @@ package org.labkey.targetedms.parser; import com.google.common.collect.Iterables; +import com.google.protobuf.InvalidProtocolBufferException; import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; import org.apache.logging.log4j.Logger; @@ -63,6 +64,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Function; import java.util.regex.Pattern; @@ -157,6 +159,7 @@ public class SkylineDocumentParser implements AutoCloseable private static final String CHARGE = "charge" ; public static final String TRANSITION_DATA = "transition_data"; private static final String LINKED_FRAGMENT_ION = "linked_fragment_ion"; + private static final String SPECTRUM_FILTER = "spectrum_filter"; private static final double MIN_SUPPORTED_VERSION = 1.2; public static final double MAX_SUPPORTED_VERSION = 23.1; @@ -1860,7 +1863,7 @@ private MoleculePrecursor readMoleculePrecursor(XMLStreamReader reader, Molecule String charge = reader.getAttributeValue(null, CHARGE); if(null != charge) moleculePrecursor.setCharge(Integer.parseInt(charge)); - + List spectrumFilterClauses = new ArrayList<>(); while(reader.hasNext()) { int evtType = reader.next(); @@ -1879,8 +1882,11 @@ else if (XmlUtil.isStartElement(reader, evtType, ANNOTATION)) annotations.add(readAnnotation(reader, new PrecursorAnnotation())); else if (XmlUtil.isStartElement(reader, evtType, NOTE)) moleculePrecursor.setNote(readNote(reader)); + else if (XmlUtil.isStartElement(reader, evtType, SPECTRUM_FILTER)) + spectrumFilterClauses.add(SpectrumFilter.FilterClause.parse(reader)); } - + moleculePrecursor.setSpectrumFilter(SpectrumFilter.fromFilterClauses(spectrumFilterClauses) + .map(SpectrumFilter::toByteArray).orElse(null)); List chromatograms = tryLoadChromatogram(moleculeTransitionList, molecule, moleculePrecursor, _matchTolerance); populateChromInfoChromatograms(moleculePrecursor, chromatograms); @@ -2035,7 +2041,7 @@ private Precursor readPrecursor(XMLStreamReader reader, Peptide peptide, boolean precursor.setExplicitCcsSqa(XmlUtil.readDoubleAttribute(reader, "explicit_ccs_sqa")); precursor.setExplicitCompensationVoltage(XmlUtil.readDoubleAttribute(reader, "explicit_compensation_voltage")); precursor.setPrecursorConcentration(XmlUtil.readDoubleAttribute(reader, "precursor_concentration")); - + List spectrumFilterClauses = new ArrayList<>(); while (reader.hasNext()) { int evtType = reader.next(); @@ -2083,8 +2089,15 @@ else if (XmlUtil.isStartElement(reader, evtType, NOTE)) { precursor.setNote(readNote(reader)); } + else if (XmlUtil.isStartElement(reader, evtType, SPECTRUM_FILTER)) + { + spectrumFilterClauses.add(SpectrumFilter.FilterClause.parse(reader)); + } } + precursor.setSpectrumFilter(SpectrumFilter.fromFilterClauses(spectrumFilterClauses) + .map(SpectrumFilter::toByteArray).orElse(null)); + // Boolean type annotations are not listed in the .sky file if their value was false. // We would still like to store them in the database. List missingBooleanAnnotations = _dataSettings.getMissingBooleanAnnotations(annotations, @@ -3113,7 +3126,6 @@ public List getSampleFileChromInfos(Map { return Collections.emptyList(); } - int traceMetricIndex = 1; for (ChromGroupHeaderInfo chromatogram : _binaryParser.getChromatograms()) { // Sample-scoped chromatograms have a magic precursor MZ value @@ -3145,14 +3157,7 @@ public List getSampleFileChromInfos(Map ChromatogramGroupId chromatogramGroupId = _binaryParser.getTextId(chromatogram); if (chromatogramGroupId != null) { - if (chromatogramGroupId.getQcTraceName() == null && chromatogram.getFlagValues().contains(ChromGroupHeaderInfo.FlagValues.extracted_qc_trace)) - { - info.setTextId("QC Trace " + traceMetricIndex++); - } - else - { - info.setTextId(chromatogramGroupId.getQcTraceName()); - } + info.setTextId(chromatogramGroupId.getQcTraceName()); } info.setChromatogramFormat(chromatogram.getChromatogramBinaryFormat().ordinal()); info.setChromatogramOffset(chromatogram.getLocationPoints()); @@ -3204,8 +3209,24 @@ private List tryLoadChromatogram( ChromGroupHeaderInfo chrom = _binaryParser.getChromatograms()[i++]; // Sequence matching for extracted chromatogram data added in v1.5 ChromatogramGroupId chromTextId = _binaryParser.getTextId(chrom); - if (chromTextId != null && !molecule.targetMatches(chromTextId.getTarget())) - continue; + if (chromTextId != null) + { + if (!molecule.targetMatches(chromTextId.getTarget())) + continue; + try + { + SpectrumFilter spectrumFilter = SpectrumFilter.fromByteArray(precursor.getSpectrumFilter()); + if (!Objects.equals(spectrumFilter, chromTextId.getSpectrumFilter())) + { + continue; + } + } + catch (InvalidProtocolBufferException e) + { + _log.warn("Error parsing spectrum filter {}", e); + return Collections.emptyList(); + } + } // If explicit retention time info is available, use that to discard obvious mismatches if (explicitRT == null || !chrom.excludesTime(explicitRT)) diff --git a/src/org/labkey/targetedms/parser/SpectrumFilter.java b/src/org/labkey/targetedms/parser/SpectrumFilter.java new file mode 100644 index 000000000..0c329795e --- /dev/null +++ b/src/org/labkey/targetedms/parser/SpectrumFilter.java @@ -0,0 +1,295 @@ +/* + * Copyright (c) 2024 LabKey Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.labkey.targetedms.parser; + +import com.google.protobuf.InvalidProtocolBufferException; +import org.junit.Assert; +import org.junit.Test; +import org.labkey.targetedms.parser.proto.ChromatogramGroupDataOuterClass; +import org.labkey.targetedms.parser.proto.ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.FilterOperation; + +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Specifies that a precursor's chromatogram should be extracted from a subset of the matching spectra. + * A SpectrumFilter contains a list of {@link FilterClause} objects which are combined with "OR" semantics. + * The individual {@link FilterPredicate} items with the FilterClause are combined with "AND". + */ +public class SpectrumFilter +{ + private static final Map _filterOperationMap = Map.ofEntries( + Map.entry("equals", FilterOperation.FILTER_OP_EQUALS), + Map.entry("<>", FilterOperation.FILTER_OP_NOT_EQUALS), + Map.entry("isnullorblank", FilterOperation.FILTER_OP_IS_BLANK), + Map.entry("isnotnullorblank", FilterOperation.FILTER_OP_IS_NOT_BLANK), + Map.entry(">", FilterOperation.FILTER_OP_IS_GREATER_THAN), + Map.entry("<", FilterOperation.FILTER_OP_IS_LESS_THAN), + Map.entry(">=", FilterOperation.FILTER_OP_IS_GREATER_THAN_OR_EQUAL_TO), + Map.entry("<=", FilterOperation.FILTER_OP_IS_LESS_THAN_OR_EQUAL_TO), + Map.entry("contains", FilterOperation.FILTER_OP_CONTAINS), + Map.entry("notcontains", FilterOperation.FILTER_OP_NOT_CONTAINS), + Map.entry("startswith", FilterOperation.FITLER_OP_STARTS_WITH), + Map.entry("notstartswith", FilterOperation.FILTER_OP_NOT_STARTS_WITH)); + private static final Map _filterOperationReverseMap = _filterOperationMap.entrySet() + .stream().collect(Collectors.toMap(Map.Entry::getValue, Map.Entry::getKey)); + private final List _filterClauses; + + public static Optional fromFilterClauses(List filterClauses) + { + if (filterClauses == null || filterClauses.isEmpty()) + { + return Optional.empty(); + } + return Optional.of(new SpectrumFilter(filterClauses)); + } + + public SpectrumFilter(List filterClauses) + { + _filterClauses = filterClauses; + } + + public static SpectrumFilter fromProtocolMessage(ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto protocolMessage) + { + List filterClauses = new ArrayList<>(); + for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter clause : protocolMessage.getFiltersList()) + { + filterClauses.add(FilterClause.fromProtocolMessage(clause)); + } + return new SpectrumFilter(filterClauses); + } + + /** + * Constructs a SpectrumFilter from the bytes that are stored in the {@link GeneralPrecursor#getSpectrumFilter()}. + */ + public static SpectrumFilter fromByteArray(byte[] byteArray) throws InvalidProtocolBufferException + { + if (byteArray == null || byteArray.length == 0) + { + return null; + } + return fromProtocolMessage(ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.parseFrom(byteArray)); + } + + public List getFilterClauses() + { + return _filterClauses; + } + + public ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto toProtocolMessage() + { + return ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.newBuilder().addAllFilters(_filterClauses.stream().map(FilterClause::toProtocolMessage).collect(Collectors.toList())).build(); + } + + public byte[] toByteArray() + { + return toProtocolMessage().toByteArray(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SpectrumFilter that = (SpectrumFilter) o; + return Objects.equals(_filterClauses, that._filterClauses); + } + + @Override + public int hashCode() + { + return Objects.hashCode(_filterClauses); + } + + public static class FilterClause + { + private final List _predicates; + + public FilterClause(List predicates) + { + _predicates = predicates; + } + + public static FilterClause fromProtocolMessage(ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter protocolMessage) + { + List predicates = new ArrayList<>(); + for (ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter.Predicate predicate : protocolMessage.getPredicatesList()) + { + predicates.add(new FilterPredicate(predicate.getPropertyPath(), _filterOperationReverseMap.get(predicate.getOperation()), predicate.getOperand())); + } + return new FilterClause(predicates); + } + + public static FilterClause parse(XMLStreamReader reader) throws XMLStreamException + { + String elementName = reader.getLocalName(); + List predicates = new ArrayList<>(); + while (reader.hasNext()) + { + int evtType = reader.next(); + + if (evtType == XMLStreamReader.END_ELEMENT && elementName.equals(reader.getLocalName())) break; + + if (XmlUtil.isStartElement(reader, evtType, "filter")) + { + predicates.add(new FilterPredicate(XmlUtil.readAttribute(reader, "column"), + XmlUtil.readAttribute(reader, "opname"), + XmlUtil.readAttribute(reader, "operand"))); + } + } + return new FilterClause(predicates); + } + + public List getPredicates() + { + return _predicates; + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FilterClause that = (FilterClause) o; + return Objects.equals(_predicates, that._predicates); + } + + @Override + public int hashCode() + { + return Objects.hashCode(_predicates); + } + + public ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter toProtocolMessage() + { + + return ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter.newBuilder() + .addAllPredicates(_predicates.stream().map(FilterPredicate::toProtocolMessage) + .collect(Collectors.toList())).build(); + } + } + + public static class FilterPredicate + { + private final String _column; + private final String _operation; + private final String _operand; + + public FilterPredicate(String column, String operation, String operand) + { + _column = column; + _operation = operation; + _operand = "".equals(operand) ? null : operand; + } + + public String getColumn() + { + return _column; + } + + public String getOperation() + { + return _operation; + } + + public String getOperand() + { + return _operand; + } + + public ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter.Predicate toProtocolMessage() + { + return ChromatogramGroupDataOuterClass.ChromatogramGroupIdsProto.SpectrumFilter.Predicate.newBuilder() + .setPropertyPath(getColumn()).setOperation(_filterOperationMap.get(getOperation())) + .setOperand(_operand == null ? "" : _operand).build(); + } + + @Override + public boolean equals(Object o) + { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + FilterPredicate that = (FilterPredicate) o; + return Objects.equals(_column, that._column) && + Objects.equals(_operation, that._operation) && + Objects.equals(_operand, that._operand); + } + + @Override + public int hashCode() + { + return Objects.hash(_column, _operation, _operand); + } + } + + public static class TestCase + { + @Test + public void testParse() throws Exception + { + String xml = String.join("\n", + " ", + " ", + " ", + " \" operand=\"3\" />", + " ", + " ", + " ", + " ", + " ", + " ", + " "); + XMLStreamReader reader = XMLInputFactory.newInstance().createXMLStreamReader(new StringReader(xml)); + int bibliospecSpectrumInfoCount = 0; + List clauses = new ArrayList<>(); + while (reader.hasNext()) + { + int event = reader.next(); + if (XmlUtil.isEndElement(reader, event, "precursor")) + { + break; + } + if (XmlUtil.isStartElement(reader, event, "spectrum_filter")) + { + clauses.add(FilterClause.parse(reader)); + } + else if (XmlUtil.isStartElement(reader, event, "bibliospec_spectrum_info")) + { + bibliospecSpectrumInfoCount++; + } + } + Assert.assertEquals(2, clauses.size()); + SpectrumFilter spectrumFilter = new SpectrumFilter(clauses); + Assert.assertEquals(1, bibliospecSpectrumInfoCount); + Assert.assertNotNull(spectrumFilter); + Assert.assertEquals(2, spectrumFilter.getFilterClauses().size()); + byte[] bytes = spectrumFilter.toByteArray(); + SpectrumFilter spectrumFilterRoundTrip = SpectrumFilter.fromByteArray(bytes); + Assert.assertNotNull(spectrumFilterRoundTrip); + Assert.assertEquals(spectrumFilter.getFilterClauses().size(), spectrumFilterRoundTrip.getFilterClauses().size()); + Assert.assertArrayEquals(spectrumFilter.toByteArray(), spectrumFilterRoundTrip.toByteArray()); + } + } +} diff --git a/test/sampledata/TargetedMS/DocumentFormats/SpectrumFilterTest.sky.zip b/test/sampledata/TargetedMS/DocumentFormats/SpectrumFilterTest.sky.zip new file mode 100644 index 0000000000000000000000000000000000000000..6ddee6b6173cf01fe831e01d1e5cd7404c990106 GIT binary patch literal 42948 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