1 package org.broadinstitute.hellbender.engine;
2 
3 import htsjdk.samtools.SAMFileHeader;
4 import htsjdk.samtools.SAMReadGroupRecord;
5 import htsjdk.samtools.util.Locatable;
6 import org.broadinstitute.hellbender.utils.HasGenomeLocation;
7 import org.broadinstitute.hellbender.utils.Utils;
8 import org.broadinstitute.hellbender.utils.pileup.ReadPileup;
9 
10 import java.util.Collection;
11 import java.util.Collections;
12 import java.util.LinkedHashMap;
13 import java.util.Map;
14 
15 /**
16  * Bundles together a pileup and a location.
17  */
18 public final class AlignmentContext implements Locatable, HasGenomeLocation {
19     // Definitions:
20     //   COMPLETE = full alignment context
21     //   FORWARD  = reads on forward strand
22     //   REVERSE  = reads on reverse strand
23     //
24     public enum ReadOrientation { COMPLETE, FORWARD, REVERSE }
25 
26     private final Locatable loc;
27     private final ReadPileup basePileup;
28 
29     private boolean hasPileupBeenDownsampled;
30 
AlignmentContext(final Locatable loc, final ReadPileup basePileup)31     public AlignmentContext(final Locatable loc, final ReadPileup basePileup) {
32         this(loc, basePileup, false);
33     }
34 
AlignmentContext(final Locatable loc, final ReadPileup basePileup, final boolean hasPileupBeenDownsampled )35     public AlignmentContext(final Locatable loc, final ReadPileup basePileup, final boolean hasPileupBeenDownsampled ) {
36         Utils.nonNull(loc, "BUG: GenomeLoc in Alignment context is null");
37         Utils.nonNull(basePileup, "BUG: ReadBackedPileup in Alignment context is null");
38 
39         this.loc = loc;
40         this.basePileup = basePileup;
41         this.hasPileupBeenDownsampled = hasPileupBeenDownsampled;
42     }
43 
44     /**
45      * How many reads cover this locus?
46      * @return
47      */
size()48     public int size() {
49         return basePileup.size();
50     }
51 
52     @Override
getContig()53     public String getContig() {
54         return getLocation().getContig();
55     }
56 
57     @Override
getStart()58     public int getStart() {
59         return getLocation().getStart();
60     }
61 
62     @Override
getEnd()63     public int getEnd() {
64         return getLocation().getEnd();
65     }
66 
getPosition()67     public long getPosition() { return getStart(); }
68 
69     @Override
getLocation()70     public Locatable getLocation() { return loc; }
71 
72     /**
73      * Returns true if any reads have been filtered out of the pileup due to excess DoC.
74      * @return True if reads have been filtered out.  False otherwise.
75      */
hasPileupBeenDownsampled()76     public boolean hasPileupBeenDownsampled() { return hasPileupBeenDownsampled; }
77 
getBasePileup()78     public ReadPileup getBasePileup() {
79         return basePileup;
80     }
81 
82     /**
83      * Returns a potentially derived subcontext containing only forward, reverse, or in fact all reads
84      * in alignment context context.
85      */
stratify(final ReadOrientation type)86     public AlignmentContext stratify(final ReadOrientation type) {
87         switch(type) {
88             case COMPLETE:
89                 return this;
90             case FORWARD:
91                 return new AlignmentContext(loc, basePileup.makeFilteredPileup(pe -> !pe.getRead().isReverseStrand()));
92             case REVERSE:
93                 return new AlignmentContext(loc, basePileup.makeFilteredPileup(pe -> pe.getRead().isReverseStrand()));
94             default:
95                 throw new IllegalArgumentException("Unable to get alignment context for type = " + type);
96         }
97     }
98 
splitContextBySampleName(final SAMFileHeader header)99     public Map<String, AlignmentContext> splitContextBySampleName(final SAMFileHeader header) {
100         return this.splitContextBySampleName((String) null, header);
101     }
102 
103     /**
104      * Splits the given AlignmentContext into a StratifiedAlignmentContext per sample, but referenced by sample name instead
105      * of sample object.
106      *
107      * @param assumedSingleSample If non-null, assume this is the only sample in our pileup and return immediately.
108      *                            If null, get the list of samples from the provided header and do the work of splitting by sample.
109      * @return a Map of sample name to StratifiedAlignmentContext; samples without coverage are not included
110      **/
splitContextBySampleName(final String assumedSingleSample, final SAMFileHeader header)111     public Map<String, AlignmentContext> splitContextBySampleName(final String assumedSingleSample, final SAMFileHeader header) {
112         if (assumedSingleSample != null){
113             return Collections.singletonMap(assumedSingleSample, this);
114         }
115         final Locatable loc = this.getLocation();
116         // this will throw an user error if there are samples without RG/sampleName
117         final Map<String, ReadPileup> pileups = this.getBasePileup().splitBySample(header, assumedSingleSample);
118         final Map<String, AlignmentContext> contexts = new LinkedHashMap<>(pileups.size());
119         for (final Map.Entry<String, ReadPileup> entry : pileups.entrySet()) {
120             // Don't add empty pileups to the split context.
121             if (entry.getValue().isEmpty()) {
122                 continue;
123             }
124             contexts.put(entry.getKey(), new AlignmentContext(loc, entry.getValue()));
125         }
126         return contexts;
127     }
128 
splitContextBySampleName(final ReadPileup pileup, final SAMFileHeader header)129     public static Map<String, AlignmentContext> splitContextBySampleName(final ReadPileup pileup, final SAMFileHeader header) {
130         return new AlignmentContext(pileup.getLocation(), pileup).splitContextBySampleName(header);
131     }
132 
133     @Override
toString()134     public String toString() {
135         return "AlignmentContext{" +
136                 "loc=" + loc +
137                 ", basePileup=" + basePileup +
138                 ", hasPileupBeenDownsampled=" + hasPileupBeenDownsampled +
139                 '}';
140     }
141 }
142