diff --git a/pom.xml b/pom.xml
index e314a338..0843f8d8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -95,72 +95,9 @@
META-INF/*.RSA
-
- it.unimi.dsi:fastutil
-
-
-
- it/unimi/dsi/fastutil/booleans/BooleanArrays**
- it/unimi/dsi/fastutil/booleans/BooleanComparator**
-
- it/unimi/dsi/fastutil/chars/AbstractCharBidirectionalIterator**
- it/unimi/dsi/fastutil/chars/AbstractCharCollection**
- it/unimi/dsi/fastutil/chars/AbstractCharIterator**
- it/unimi/dsi/fastutil/chars/AbstractCharList**
- it/unimi/dsi/fastutil/chars/AbstractCharListIterator**
- it/unimi/dsi/fastutil/chars/AbstractCharSet**
- it/unimi/dsi/fastutil/chars/CharArrayList**
- it/unimi/dsi/fastutil/chars/CharArrays**
- it/unimi/dsi/fastutil/chars/CharBidirectionalIterator**
- it/unimi/dsi/fastutil/chars/CharCollection**
- it/unimi/dsi/fastutil/chars/CharComparator**
- it/unimi/dsi/fastutil/chars/CharIterable**
- it/unimi/dsi/fastutil/chars/CharIterator**
- it/unimi/dsi/fastutil/chars/CharIterators**
- it/unimi/dsi/fastutil/chars/CharList**
- it/unimi/dsi/fastutil/chars/CharListIterator**
- it/unimi/dsi/fastutil/chars/CharOpenHashSet**
- it/unimi/dsi/fastutil/chars/CharSet**
- it/unimi/dsi/fastutil/chars/CharStack**
-
- it/unimi/dsi/fastutil/ints/IntArrays**
- it/unimi/dsi/fastutil/ints/IntComparator**
-
- it/unimi/dsi/fastutil/objects/AbstractObjectBidirectionalIterator**
- it/unimi/dsi/fastutil/objects/AbstractObjectCollection**
- it/unimi/dsi/fastutil/objects/AbstractObjectIterator**
- it/unimi/dsi/fastutil/objects/AbstractObjectList**
- it/unimi/dsi/fastutil/objects/AbstractObjectListIterator**
- it/unimi/dsi/fastutil/objects/ObjectArrayList**
- it/unimi/dsi/fastutil/objects/ObjectArrays**
- it/unimi/dsi/fastutil/objects/ObjectBidirectionalIterator**
- it/unimi/dsi/fastutil/objects/ObjectCollection**
- it/unimi/dsi/fastutil/objects/ObjectIterable**
- it/unimi/dsi/fastutil/objects/ObjectIterator**
- it/unimi/dsi/fastutil/objects/ObjectIterators**
- it/unimi/dsi/fastutil/objects/ObjectList**
- it/unimi/dsi/fastutil/objects/ObjectListIterator**
-
- it/unimi/dsi/fastutil/Arrays**
- it/unimi/dsi/fastutil/BidirectionalIterator**
- it/unimi/dsi/fastutil/Hash**
- it/unimi/dsi/fastutil/HashCommon**
- it/unimi/dsi/fastutil/Stack**
- it/unimi/dsi/fastutil/Swapper**
-
-
- it/unimi/dsi/fastutil/chars/AbstractChar2**
- it/unimi/dsi/fastutil/chars/Char2**
- it/unimi/dsi/fastutil/ints/IntComparators**
- it/unimi/dsi/fastutil/objects/ObjectCollections**
- it/unimi/dsi/fastutil/objects/ObjectLists**
- it/unimi/dsi/fastutil/chars/CharCollections**
- it/unimi/dsi/fastutil/chars/CharComparators**
- it/unimi/dsi/fastutil/chars/CharLists**
- it/unimi/dsi/fastutil/chars/CharSets**
-
-
+
*:*
diff --git a/src/main/java/edu/ucsd/msjava/msdbsearch/SearchParams.java b/src/main/java/edu/ucsd/msjava/msdbsearch/SearchParams.java
index 55982a06..5b9febbe 100644
--- a/src/main/java/edu/ucsd/msjava/msdbsearch/SearchParams.java
+++ b/src/main/java/edu/ucsd/msjava/msdbsearch/SearchParams.java
@@ -51,6 +51,8 @@ public class SearchParams {
private int maxMissedCleavages;
private int maxNumMods;
private boolean allowDenseCentroidedPeaks;
+ private int minMSLevel;
+ private int maxMSLevel;
public SearchParams() {
}
@@ -220,6 +222,16 @@ public boolean getAllowDenseCentroidedPeaks() {
return allowDenseCentroidedPeaks;
}
+ // Used by MS-GF+
+ public int getMinMSLevel() {
+ return minMSLevel;
+ }
+
+ // Used by MS-GF+
+ public int getMaxMSLevel() {
+ return maxMSLevel;
+ }
+
/**
* Look for # in dataLine
* If present, remove that character and any comment after it
@@ -410,6 +422,10 @@ public String parse(ParamManager paramManager) {
allowDenseCentroidedPeaks = paramManager.getAllowDenseCentroidedPeaks() == 1;
+ IntRangeParameter msLevelParam = paramManager.getMSLevelParameter();
+ minMSLevel = msLevelParam.getMin();
+ maxMSLevel = msLevelParam.getMax();
+
maxNumMods = paramManager.getMaxNumModsPerPeptide();
int maxNumModsCompare = aaSet.getMaxNumberOfVariableModificationsPerPeptide();
@@ -594,6 +610,7 @@ public String toString() {
buf.append(" (custom)\n");
}
+ buf.append("\tMSLevel: " + this.minMSLevel + "," + this.maxMSLevel + "\n");
buf.append("\tMinNumPeaksPerSpectrum: " + this.minNumPeaksPerSpectrum + "\n");
buf.append("\tNumIsoforms: " + this.maxNumVariantsPerPeptide + "\n");
diff --git a/src/main/java/edu/ucsd/msjava/msutil/SpecKey.java b/src/main/java/edu/ucsd/msjava/msutil/SpecKey.java
index 796b3af9..308759eb 100644
--- a/src/main/java/edu/ucsd/msjava/msutil/SpecKey.java
+++ b/src/main/java/edu/ucsd/msjava/msutil/SpecKey.java
@@ -68,7 +68,9 @@ public static ArrayList getSpecKeyList(
int maxCharge,
ActivationMethod activationMethod,
int minNumPeaksPerSpectrum,
- boolean allowDenseCentroidedData) {
+ boolean allowDenseCentroidedData,
+ int minMSLevel,
+ int maxMSLevel) {
Iterator itr = specAcc.getSpecItr();
@@ -80,7 +82,9 @@ public static ArrayList getSpecKeyList(
maxCharge,
activationMethod,
minNumPeaksPerSpectrum,
- allowDenseCentroidedData);
+ allowDenseCentroidedData,
+ minMSLevel,
+ maxMSLevel);
SpectrumParser parser = specAcc.getSpectrumParser();
@@ -104,7 +108,9 @@ public static ArrayList getSpecKeyList(
int maxCharge,
ActivationMethod activationMethod,
int minNumPeaksPerSpectrum,
- boolean allowDenseCentroidedData) {
+ boolean allowDenseCentroidedData,
+ int minMSLevel,
+ int maxMSLevel) {
if (activationMethod == ActivationMethod.FUSION)
return getFusedSpecKeyList(itr, startSpecIndex, endSpecIndex, minCharge, maxCharge);
@@ -114,6 +120,7 @@ public static ArrayList getSpecKeyList(
int numProfileSpectra = 0;
int numDenseCentroidedSpectra = 0;
int numSpectraWithTooFewPeaks = 0;
+ int numFilteredByMSLevel = 0;
final int MAX_INFORMATIVE_MESSAGES = 10;
int informativeMessageCount = 0;
@@ -126,6 +133,11 @@ public static ArrayList getSpecKeyList(
if (specIndex >= endSpecIndex)
continue;
+ if (spec.getMSLevel() < minMSLevel || spec.getMSLevel() > maxMSLevel) {
+ numFilteredByMSLevel++;
+ continue;
+ }
+
spec.setChargeIfSinglyCharged();
int charge = spec.getCharge();
ActivationMethod specActivationMethod = spec.getActivationMethod();
@@ -217,6 +229,9 @@ public static ArrayList getSpecKeyList(
}
System.out.println("Ignoring " + numProfileSpectra + " profile spectra.");
+ if (numFilteredByMSLevel > 0) {
+ System.out.println("Ignoring " + numFilteredByMSLevel + " spectra with MS level outside range [" + minMSLevel + "," + maxMSLevel + "].");
+ }
System.out.println("Ignoring " + numSpectraWithTooFewPeaks + " spectra having less than " + minNumPeaksPerSpectrum + " peaks.");
if (numDenseCentroidedSpectra > 0) {
System.out.println("Ignoring " + numDenseCentroidedSpectra + " spectra marked as centroid with dense peaks (<50ppm median distance).\n" +
diff --git a/src/main/java/edu/ucsd/msjava/msutil/SpectraAccessor.java b/src/main/java/edu/ucsd/msjava/msutil/SpectraAccessor.java
index 846e121b..1854b6a2 100644
--- a/src/main/java/edu/ucsd/msjava/msutil/SpectraAccessor.java
+++ b/src/main/java/edu/ucsd/msjava/msutil/SpectraAccessor.java
@@ -20,6 +20,9 @@ public class SpectraAccessor {
private MzMLAdapter mzmlAdapter = null;
+ private int minMSLevel = 2;
+ private int maxMSLevel = 2;
+
SpectrumAccessorBySpecIndex specMap = null;
Iterator specItr = null;
@@ -45,13 +48,25 @@ public SpectraAccessor(File specFile, SpecFileFormat specFormat) {
this.spectrumParser = null;
}
+ /**
+ * Set the MS level range for spectrum filtering (both inclusive).
+ *
+ * @param minMSLevel minimum MS level to consider (inclusive).
+ * @param maxMSLevel maximum MS level to consider (inclusive).
+ */
+ public void setMSLevelRange(int minMSLevel, int maxMSLevel) {
+ this.minMSLevel = minMSLevel;
+ this.maxMSLevel = maxMSLevel;
+ }
+
public SpectrumAccessorBySpecIndex getSpecMap() {
if (specMap == null) {
if (specFormat == SpecFileFormat.MZXML)
- specMap = new MzXMLSpectraMap(specFile.getPath());
+ specMap = new MzXMLSpectraMap(specFile.getPath()).msLevel(minMSLevel, maxMSLevel);
else if (specFormat == SpecFileFormat.MZML) {
if (mzmlAdapter == null)
mzmlAdapter = new MzMLAdapter(specFile);
+ mzmlAdapter.msLevel(minMSLevel, maxMSLevel);
specMap = new MzMLSpectraMap(mzmlAdapter);
} else if (specFormat == SpecFileFormat.DTA_TXT)
specMap = new PNNLSpectraMap(specFile.getPath());
@@ -82,10 +97,11 @@ else if (specFormat == SpecFileFormat.PKL)
public Iterator getSpecItr() {
if (specItr == null) {
if (specFormat == SpecFileFormat.MZXML)
- specItr = new MzXMLSpectraIterator(specFile.getPath());
+ specItr = new MzXMLSpectraIterator(specFile.getPath(), minMSLevel, maxMSLevel);
else if (specFormat == SpecFileFormat.MZML) {
if (mzmlAdapter == null)
mzmlAdapter = new MzMLAdapter(specFile);
+ mzmlAdapter.msLevel(minMSLevel, maxMSLevel);
specItr = new MzMLSpectraIterator(mzmlAdapter);
} else if (specFormat == SpecFileFormat.DTA_TXT)
try {
diff --git a/src/main/java/edu/ucsd/msjava/mzid/MZIdentMLGen.java b/src/main/java/edu/ucsd/msjava/mzid/MZIdentMLGen.java
index 1cc5352d..56728cbc 100644
--- a/src/main/java/edu/ucsd/msjava/mzid/MZIdentMLGen.java
+++ b/src/main/java/edu/ucsd/msjava/mzid/MZIdentMLGen.java
@@ -260,6 +260,10 @@ public synchronized void addSpectrumIdentificationResults(List re
continue;
edu.ucsd.msjava.msutil.Spectrum spec = specAcc.getSpecMap().getSpectrumBySpecIndex(specIndex);
+ if (spec == null) {
+ System.err.println("Warning: spectrum index " + specIndex + " not found in spectrum file; skipping");
+ continue;
+ }
String specID = spec.getID();
float precursorMz = spec.getPrecursorPeak().getMz();
@@ -322,7 +326,7 @@ public synchronized void addSpectrumIdentificationResults(List re
DatabaseMatch match = matchList.get(i);
if (match.getDeNovoScore() < params.getMinDeNovoScore())
- break;
+ continue;
// int pepIndex = match.getIndex(); // Position of preAA
int length = match.getLength(); // Peptide length + 2
diff --git a/src/main/java/edu/ucsd/msjava/mzml/MzMLAdapter.java b/src/main/java/edu/ucsd/msjava/mzml/MzMLAdapter.java
index 808c24fd..61f37af7 100644
--- a/src/main/java/edu/ucsd/msjava/mzml/MzMLAdapter.java
+++ b/src/main/java/edu/ucsd/msjava/mzml/MzMLAdapter.java
@@ -18,7 +18,7 @@ public class MzMLAdapter {
private final File specFile;
private MzMLUnmarshaller unmarshaller;
private int minMSLevel = 2; // inclusive
- private int maxMSLevel = Integer.MAX_VALUE; // exclusive
+ private int maxMSLevel = Integer.MAX_VALUE; // inclusive
private CvParam spectrumIDFormatCvParam = null;
public MzMLAdapter(File specFile) {
diff --git a/src/main/java/edu/ucsd/msjava/params/IntRangeParameter.java b/src/main/java/edu/ucsd/msjava/params/IntRangeParameter.java
index 8f772c89..309ae0ca 100644
--- a/src/main/java/edu/ucsd/msjava/params/IntRangeParameter.java
+++ b/src/main/java/edu/ucsd/msjava/params/IntRangeParameter.java
@@ -19,16 +19,14 @@ public IntRangeParameter(String key, String name, String description) {
public String parse(String value) {
String[] token = value.split(",");
try {
-// if(token.length == 1)
-// {
-// min = Integer.parseInt(token[0]);
-// max = min;
-// }
- if (token.length == 2) {
+ if (token.length == 1) {
+ min = Integer.parseInt(token[0]);
+ max = min;
+ } else if (token.length == 2) {
min = Integer.parseInt(token[0]);
max = Integer.parseInt(token[1]);
} else {
- return "illegar syntax";
+ return "illegal syntax";
}
} catch (NumberFormatException e) {
return "not a valid integer or integer range";
diff --git a/src/main/java/edu/ucsd/msjava/params/ParamManager.java b/src/main/java/edu/ucsd/msjava/params/ParamManager.java
index 2bb29ce9..89cae434 100644
--- a/src/main/java/edu/ucsd/msjava/params/ParamManager.java
+++ b/src/main/java/edu/ucsd/msjava/params/ParamManager.java
@@ -122,6 +122,11 @@ public enum ParamNameEnum {
SPEC_INDEX("index", "SpecIndex", "Range of spectrum indices to be considered",
"For example, to analyze the first 1000 spectra use -index 1,1000"),
+ MS_LEVEL("msLevel", "MSLevel", "MS level or range of MS levels to consider; Default: 2",
+ "Accepts a single value or a comma-separated range.\n" +
+ "\t For example, -msLevel 2 to search only MS2 spectra\n" +
+ "\t Or -msLevel 2,3 to search both MS2 and MS3 spectra"),
+
MAX_MISSED_CLEAVAGES("maxMissedCleavages", "MaxMissedCleavages", "Exclude peptides with more than this number of missed cleavages from the search; Default: -1 (no limit)", null),
TDA_STRATEGY("tda", "TDA", "Target decoy strategy",
@@ -695,6 +700,14 @@ private void addSpecIndexRangeParam(boolean isHidden) {
addParameter(specIndexParam);
}
+ private void addMSLevelParam() {
+ IntRangeParameter msLevelParam = new IntRangeParameter(ParamNameEnum.MS_LEVEL);
+ msLevelParam.minValue(1);
+ msLevelParam.setMaxInclusive();
+ msLevelParam.defaultValue("2,2");
+ addParameter(msLevelParam);
+ }
+
private void addEdgeScoreParam(boolean isHidden) {
EnumParameter edgeScoreParam = new EnumParameter(ParamNameEnum.EDGE_SCORE.key);
edgeScoreParam.registerEntry("Use edge scoring").setDefault();
@@ -792,6 +805,7 @@ public void addMSGFPlusParams() {
addMaxNumModsParam();
addAllowDenseCentroidedPeaksParam();
+ addMSLevelParam();
addExample("Example (high-precision): java -Xmx3500M -jar MSGFPlus.jar -s test.mzML -d IPI_human_3.79.fasta -inst 1 -t 20ppm -ti -1,2 -ntt 2 -tda 1 -o testMSGFPlus.mzid -mod Mods.txt");
addExample("Example (low-precision): java -Xmx3500M -jar MSGFPlus.jar -s test.mzML -d IPI_human_3.79.fasta -inst 0 -t 0.5Da,2.5Da -ntt 2 -tda 1 -o testMSGFPlus.mzid -mod Mods.txt");
@@ -1103,6 +1117,10 @@ public IntRangeParameter getSpecIndexParameter() {
return ((IntRangeParameter) getParameter(ParamNameEnum.SPEC_INDEX.key));
}
+ public IntRangeParameter getMSLevelParameter() {
+ return ((IntRangeParameter) getParameter(ParamNameEnum.MS_LEVEL.key));
+ }
+
public int getTDA() {
return getIntValue(ParamNameEnum.TDA_STRATEGY.key);
}
diff --git a/src/main/java/edu/ucsd/msjava/parser/MzXMLSpectraMap.java b/src/main/java/edu/ucsd/msjava/parser/MzXMLSpectraMap.java
index a8ca4d8f..3b48c2ae 100644
--- a/src/main/java/edu/ucsd/msjava/parser/MzXMLSpectraMap.java
+++ b/src/main/java/edu/ucsd/msjava/parser/MzXMLSpectraMap.java
@@ -26,7 +26,7 @@ public class MzXMLSpectraMap implements SpectrumAccessorBySpecIndex {
// if(maxMSLevel >= minMSLevel > 0) only spectra within [minMSLevel, maxMSLevel] will be returned
private int minMSLevel = 2; // inclusive
- private int maxMSLevel = Integer.MAX_VALUE; // exclusive
+ private int maxMSLevel = Integer.MAX_VALUE; // inclusive
/***** CONSTRUCTORS *****/
/**
@@ -64,7 +64,7 @@ public Spectrum getSpectrumByScanNum(int scanNumber) {
if (scanObj == null) return null;
int msLevel = scanObj.getHeader().getMsLevel();
- if (msLevel < minMSLevel || msLevel >= maxMSLevel)
+ if (msLevel < minMSLevel || msLevel > maxMSLevel)
return null;
// get peak list array (mass, intensities) pairs
double[][] peakList = scanObj.getMassIntensityList();
@@ -168,7 +168,7 @@ public String getID(int specIndex) {
if (scanObj == null) return null;
int msLevel = scanObj.getHeader().getMsLevel();
- if (msLevel < minMSLevel || msLevel >= maxMSLevel)
+ if (msLevel < minMSLevel || msLevel > maxMSLevel)
return null;
return String.valueOf(specIndex);
}
@@ -187,7 +187,7 @@ public Float getPrecursorMz(int specIndex) {
if (scanObj == null) return null;
int msLevel = scanObj.getHeader().getMsLevel();
- if (msLevel < minMSLevel || msLevel >= maxMSLevel)
+ if (msLevel < minMSLevel || msLevel > maxMSLevel)
return null;
ScanHeader header = scanObj.getHeader();
diff --git a/src/main/java/edu/ucsd/msjava/ui/MSGFDB.java b/src/main/java/edu/ucsd/msjava/ui/MSGFDB.java
index 1fe86467..2f7c07c9 100644
--- a/src/main/java/edu/ucsd/msjava/ui/MSGFDB.java
+++ b/src/main/java/edu/ucsd/msjava/ui/MSGFDB.java
@@ -278,7 +278,7 @@ private static String runMSGFDB(File specFile, SpecFileFormat specFormat, File o
int avgPeptideMass = 2000;
int numBytesPerMass = 12;
int numSpecScannedTogether = (int) ((float) maxMemory / avgPeptideMass / numBytesPerMass);
- ArrayList specKeyList = SpecKey.getSpecKeyList(specAcc.getSpecItr(), startSpecIndex, endSpecIndex, minCharge, maxCharge, activationMethod, Constants.MIN_NUM_PEAKS_PER_SPECTRUM, allowDenseCentroidedPeaks);
+ ArrayList specKeyList = SpecKey.getSpecKeyList(specAcc.getSpecItr(), startSpecIndex, endSpecIndex, minCharge, maxCharge, activationMethod, Constants.MIN_NUM_PEAKS_PER_SPECTRUM, allowDenseCentroidedPeaks, 2, Integer.MAX_VALUE);
int specSize = specKeyList.size();
System.out.print("Reading spectra finished ");
diff --git a/src/main/java/edu/ucsd/msjava/ui/MSGFDBLib.java b/src/main/java/edu/ucsd/msjava/ui/MSGFDBLib.java
index b84dcf0f..f2eaf055 100644
--- a/src/main/java/edu/ucsd/msjava/ui/MSGFDBLib.java
+++ b/src/main/java/edu/ucsd/msjava/ui/MSGFDBLib.java
@@ -104,7 +104,7 @@ public static String runMSGFLib(ParamManager paramManager) {
int avgPeptideMass = 2000;
int numBytesPerMass = 12;
int numSpecScannedTogether = (int) ((float) maxMemory / avgPeptideMass / numBytesPerMass);
- ArrayList specKeyList = SpecKey.getSpecKeyList(specAcc.getSpecItr(), 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, activationMethod, Constants.MIN_NUM_PEAKS_PER_SPECTRUM, false);
+ ArrayList specKeyList = SpecKey.getSpecKeyList(specAcc.getSpecItr(), 0, Integer.MAX_VALUE, 0, Integer.MAX_VALUE, activationMethod, Constants.MIN_NUM_PEAKS_PER_SPECTRUM, false, 2, Integer.MAX_VALUE);
int specSize = specKeyList.size();
System.out.print("Reading spectra finished ");
diff --git a/src/main/java/edu/ucsd/msjava/ui/MSGFPlus.java b/src/main/java/edu/ucsd/msjava/ui/MSGFPlus.java
index 37f344a3..92914210 100644
--- a/src/main/java/edu/ucsd/msjava/ui/MSGFPlus.java
+++ b/src/main/java/edu/ucsd/msjava/ui/MSGFPlus.java
@@ -263,12 +263,16 @@ private static String runMSGFPlus(int ioIndex, SpecFileFormat specFormat, File o
System.out.printf("Opening %s %s\n", specFormat.getPSIName(), specFile.getName());
SpectraAccessor specAcc = new SpectraAccessor(specFile, specFormat);
+ int minMSLevel = params.getMinMSLevel();
+ int maxMSLevel = params.getMaxMSLevel();
+ specAcc.setMSLevelRange(minMSLevel, maxMSLevel);
if (specAcc.getSpecMap() == null || specAcc.getSpecItr() == null)
return "Error while parsing spectrum file: " + specFile.getPath();
ArrayList specKeyList = SpecKey.getSpecKeyList(specAcc,
- startSpecIndex, endSpecIndex, minCharge, maxCharge, activationMethod, minNumPeaksPerSpectrum, allowDenseCentroidedPeaks);
+ startSpecIndex, endSpecIndex, minCharge, maxCharge, activationMethod, minNumPeaksPerSpectrum, allowDenseCentroidedPeaks,
+ minMSLevel, maxMSLevel);
int specSize = specKeyList.size();
if (specSize == 0)
diff --git a/src/test/java/msgfplus/TestIntRangeParameter.java b/src/test/java/msgfplus/TestIntRangeParameter.java
new file mode 100644
index 00000000..67f3cacc
--- /dev/null
+++ b/src/test/java/msgfplus/TestIntRangeParameter.java
@@ -0,0 +1,94 @@
+package msgfplus;
+
+import static org.junit.Assert.*;
+
+import edu.ucsd.msjava.params.IntRangeParameter;
+import org.junit.Test;
+
+/**
+ * Tests for IntRangeParameter, which supports single values and ranges.
+ * Part of issue #159: the -msLevel parameter uses IntRangeParameter.
+ */
+public class TestIntRangeParameter {
+
+ private IntRangeParameter createInclusiveParam() {
+ IntRangeParameter p = new IntRangeParameter("test", "Test", "desc");
+ p.setMaxInclusive();
+ return p;
+ }
+
+ @Test
+ public void testSingleValue() {
+ IntRangeParameter p = createInclusiveParam();
+ String err = p.parse("2");
+ assertNull("Single value should parse successfully", err);
+ assertEquals(2, (int) p.getMin());
+ assertEquals(2, (int) p.getMax());
+ }
+
+ @Test
+ public void testRange() {
+ IntRangeParameter p = createInclusiveParam();
+ String err = p.parse("2,3");
+ assertNull("Range should parse successfully", err);
+ assertEquals(2, (int) p.getMin());
+ assertEquals(3, (int) p.getMax());
+ }
+
+ @Test
+ public void testWideRange() {
+ IntRangeParameter p = createInclusiveParam();
+ String err = p.parse("1,5");
+ assertNull(err);
+ assertEquals(1, (int) p.getMin());
+ assertEquals(5, (int) p.getMax());
+ }
+
+ @Test
+ public void testSameMinMax() {
+ IntRangeParameter p = createInclusiveParam();
+ String err = p.parse("3,3");
+ assertNull(err);
+ assertEquals(3, (int) p.getMin());
+ assertEquals(3, (int) p.getMax());
+ }
+
+ @Test
+ public void testSingleValueExclusiveMaxRejects() {
+ // Default constructor has isMaxInclusive=false, so single value "2"
+ // produces min=2,max=2 but effective maxNumber=1 < minNumber=2 -> invalid
+ IntRangeParameter p = new IntRangeParameter("test", "Test", "desc");
+ String err = p.parse("2");
+ assertNotNull("Single value with exclusive max should fail", err);
+ }
+
+ @Test
+ public void testInvalidReversedRange() {
+ IntRangeParameter p = createInclusiveParam();
+ String err = p.parse("5,2");
+ assertNotNull("Reversed range should fail", err);
+ }
+
+ @Test
+ public void testInvalidTooManyValues() {
+ IntRangeParameter p = createInclusiveParam();
+ String err = p.parse("1,2,3");
+ assertNotNull("Three values should fail", err);
+ assertEquals("illegal syntax", err);
+ }
+
+ @Test
+ public void testInvalidNonNumeric() {
+ IntRangeParameter p = createInclusiveParam();
+ String err = p.parse("abc");
+ assertNotNull("Non-numeric should fail", err);
+ assertEquals("not a valid integer or integer range", err);
+ }
+
+ @Test
+ public void testInvalidEmpty() {
+ IntRangeParameter p = createInclusiveParam();
+ String err = p.parse("");
+ assertNotNull("Empty string should fail", err);
+ }
+}
diff --git a/src/test/java/msgfplus/TestMSLevelFiltering.java b/src/test/java/msgfplus/TestMSLevelFiltering.java
new file mode 100644
index 00000000..361c574d
--- /dev/null
+++ b/src/test/java/msgfplus/TestMSLevelFiltering.java
@@ -0,0 +1,76 @@
+package msgfplus;
+
+import static org.junit.Assert.*;
+
+import edu.ucsd.msjava.params.IntRangeParameter;
+import edu.ucsd.msjava.params.ParamManager;
+import org.junit.Test;
+
+/**
+ * Tests for the -msLevel parameter (issue #159).
+ * Verifies that MS level filtering is properly wired through ParamManager.
+ */
+public class TestMSLevelFiltering {
+
+ private ParamManager createParamManager() {
+ ParamManager pm = new ParamManager("MS-GF+", "test", "2024.01.01", "test");
+ pm.addMSGFPlusParams();
+ return pm;
+ }
+
+ @Test
+ public void testMSLevelParameterExists() {
+ ParamManager pm = createParamManager();
+ IntRangeParameter msLevel = pm.getMSLevelParameter();
+ assertNotNull("MS_LEVEL parameter should exist", msLevel);
+ }
+
+ @Test
+ public void testMSLevelDefaultIsMS2() {
+ ParamManager pm = createParamManager();
+ IntRangeParameter msLevel = pm.getMSLevelParameter();
+ // Default should be MS2 only (2,2)
+ assertEquals("Default min MS level should be 2", 2, (int) msLevel.getMin());
+ assertEquals("Default max MS level should be 2", 2, (int) msLevel.getMax());
+ }
+
+ @Test
+ public void testMSLevelParseSingleValue() {
+ ParamManager pm = createParamManager();
+ IntRangeParameter msLevel = pm.getMSLevelParameter();
+ String err = msLevel.parse("2");
+ assertNull("Parsing '2' should succeed", err);
+ assertEquals(2, (int) msLevel.getMin());
+ assertEquals(2, (int) msLevel.getMax());
+ }
+
+ @Test
+ public void testMSLevelParseRange() {
+ ParamManager pm = createParamManager();
+ IntRangeParameter msLevel = pm.getMSLevelParameter();
+ String err = msLevel.parse("2,3");
+ assertNull("Parsing '2,3' should succeed", err);
+ assertEquals(2, (int) msLevel.getMin());
+ assertEquals(3, (int) msLevel.getMax());
+ }
+
+ @Test
+ public void testMSLevelParseMS3Only() {
+ ParamManager pm = createParamManager();
+ IntRangeParameter msLevel = pm.getMSLevelParameter();
+ String err = msLevel.parse("3");
+ assertNull("Parsing '3' should succeed", err);
+ assertEquals(3, (int) msLevel.getMin());
+ assertEquals(3, (int) msLevel.getMax());
+ }
+
+ @Test
+ public void testMSLevelParseWideRange() {
+ ParamManager pm = createParamManager();
+ IntRangeParameter msLevel = pm.getMSLevelParameter();
+ String err = msLevel.parse("1,5");
+ assertNull("Parsing '1,5' should succeed", err);
+ assertEquals(1, (int) msLevel.getMin());
+ assertEquals(5, (int) msLevel.getMax());
+ }
+}
diff --git a/src/test/java/msgfplus/TestMZIdentMLGen.java b/src/test/java/msgfplus/TestMZIdentMLGen.java
new file mode 100644
index 00000000..46c2e0cc
--- /dev/null
+++ b/src/test/java/msgfplus/TestMZIdentMLGen.java
@@ -0,0 +1,142 @@
+package msgfplus;
+
+import static org.junit.Assert.*;
+
+import java.io.*;
+import java.nio.file.Files;
+
+import org.junit.Test;
+
+import edu.ucsd.msjava.ui.MSGFPlus;
+
+/**
+ * Tests for issue #157: verify mzid export completeness.
+ * Ensures that all SpectrumIdentificationItems have complete score CVParams
+ * and that the break->continue fix in MZIdentMLGen preserves all valid PSMs.
+ */
+public class TestMZIdentMLGen {
+
+ /**
+ * Run a small MSGF+ search and verify that the output mzid has
+ * complete scores for every SpectrumIdentificationItem.
+ *
+ * This catches the issue #157 bug where a 'break' on low DeNovoScore
+ * would silently drop all subsequent matches for that spectrum.
+ * With the fix (continue instead of break), every valid match gets
+ * its full set of score CVParams.
+ */
+ @Test
+ public void testMzidScoreCompleteness() throws Exception {
+ File specFile = new File(getClass().getClassLoader().getResource("test.mgf").toURI());
+ File dbFile = new File(getClass().getClassLoader().getResource("Tryp_Pig_Bov.fasta").toURI());
+ File outputFile = File.createTempFile("test_157_", ".mzid");
+ outputFile.deleteOnExit();
+
+ // Use Tryp_Pig_Bov.fasta (tiny DB) for fast execution.
+ // Even with few or no PSMs, validates the code path does not crash.
+ String[] argv = {
+ "-s", specFile.getPath(),
+ "-d", dbFile.getPath(),
+ "-o", outputFile.getPath(),
+ "-t", "20ppm",
+ "-tda", "0",
+ "-ntt", "2",
+ "-thread", "2",
+ "-minLength", "6",
+ "-maxLength", "40",
+ "-minCharge", "2",
+ "-maxCharge", "4",
+ "-n", "1"
+ };
+
+ MSGFPlus.main(argv);
+
+ assertTrue("Output mzid file should exist", outputFile.exists());
+ assertTrue("Output mzid file should not be empty", outputFile.length() > 0);
+
+ // Parse the output and verify score completeness
+ String content = new String(Files.readAllBytes(outputFile.toPath()));
+
+ // Count SpectrumIdentificationItem elements (opening tags only)
+ int siiCount = countOccurrences(content, " 0) {
+ // Every SII must have all 4 score CVParams
+ assertEquals("Every SII should have a RawScore", siiCount, rawScoreCount);
+ assertEquals("Every SII should have a DeNovoScore", siiCount, deNovoScoreCount);
+ assertEquals("Every SII should have a SpecEValue", siiCount, specEValueCount);
+ assertEquals("Every SII should have an EValue", siiCount, eValueCount);
+ }
+
+ // Verify no empty SpectrumIdentificationResult (SIR without SII children)
+ // This would indicate silently dropped PSMs
+ assertFalse("Should not have empty SpectrumIdentificationResult elements",
+ content.contains("") &&
+ content.contains(""));
+
+ outputFile.delete();
+ }
+
+ /**
+ * Verify that the mzid output file is well-formed XML and contains
+ * the required mzIdentML structure elements.
+ */
+ @Test
+ public void testMzidStructuralValidity() throws Exception {
+ File specFile = new File(getClass().getClassLoader().getResource("test.mgf").toURI());
+ File dbFile = new File(getClass().getClassLoader().getResource("BSA.fasta").toURI());
+ File outputFile = File.createTempFile("test_157_struct_", ".mzid");
+ outputFile.deleteOnExit();
+
+ String[] argv = {
+ "-s", specFile.getPath(),
+ "-d", dbFile.getPath(),
+ "-o", outputFile.getPath(),
+ "-t", "20ppm",
+ "-tda", "0",
+ "-ntt", "2",
+ "-thread", "2",
+ "-n", "1"
+ };
+
+ MSGFPlus.main(argv);
+
+ assertTrue("Output mzid file should exist", outputFile.exists());
+ String content = new String(Files.readAllBytes(outputFile.toPath()));
+
+ // Verify required mzIdentML sections exist
+ assertTrue("Should contain MzIdentML root element",
+ content.contains("