private SnpEffEffect getMostSignificantEffect(List<SnpEffEffect> effects) { SnpEffEffect mostSignificantEffect = null; for (SnpEffEffect effect : effects) { if (mostSignificantEffect == null || effect.isHigherImpactThan(mostSignificantEffect)) { mostSignificantEffect = effect; } } return mostSignificantEffect; }
private List<SnpEffEffect> parseSnpEffRecord(VariantContext snpEffRecord) { List<SnpEffEffect> parsedEffects = new ArrayList<SnpEffEffect>(); Object effectFieldValue = snpEffRecord.getAttribute(SNPEFF_INFO_FIELD_KEY); if (effectFieldValue == null) { return parsedEffects; } // The VCF codec stores multi-valued fields as a List<String>, and single-valued fields as a // String. // We can have either in the case of SnpEff, since there may be one or more than one effect in // this record. List<String> individualEffects; if (effectFieldValue instanceof List) { individualEffects = (List<String>) effectFieldValue; } else { individualEffects = Arrays.asList((String) effectFieldValue); } for (String effectString : individualEffects) { String[] effectNameAndMetadata = effectString.split(SNPEFF_EFFECT_METADATA_DELIMITER); if (effectNameAndMetadata.length != 2) { logger.warn( String.format( "Malformed SnpEff effect field at %s:%d, skipping: %s", snpEffRecord.getChr(), snpEffRecord.getStart(), effectString)); continue; } String effectName = effectNameAndMetadata[0]; String[] effectMetadata = effectNameAndMetadata[1].split(SNPEFF_EFFECT_METADATA_SUBFIELD_DELIMITER, -1); SnpEffEffect parsedEffect = new SnpEffEffect(effectName, effectMetadata); if (parsedEffect.isWellFormed()) { parsedEffects.add(parsedEffect); } else { logger.warn( String.format( "Skipping malformed SnpEff effect field at %s:%d. Error was: \"%s\". Field was: \"%s\"", snpEffRecord.getChr(), snpEffRecord.getStart(), parsedEffect.getParseError(), effectString)); } } return parsedEffects; }
public boolean isHigherImpactThan(SnpEffEffect other) { // If one effect is within a coding gene and the other is not, the effect that is // within the coding gene has higher impact: if (isCoding() && !other.isCoding()) { return true; } else if (!isCoding() && other.isCoding()) { return false; } // Otherwise, both effects are either in or not in a coding gene, so we compare the impacts // of the effects themselves. Effects with the same impact are tie-broken using the // functional class of the effect: if (impact.isHigherImpactThan(other.impact)) { return true; } else if (impact.isSameImpactAs(other.impact)) { return functionalClass.isHigherPriorityThan(other.functionalClass); } return false; }
@Override public Map<String, Object> annotate( final RefMetaDataTracker tracker, final AnnotatorCompatible walker, final ReferenceContext ref, final Map<String, AlignmentContext> stratifiedContexts, final VariantContext vc, final Map<String, PerReadAlleleLikelihoodMap> stratifiedPerReadAlleleLikelihoodMap) { // Can not annotate if failed initialization conditions if (!canAnnotate) return null; RodBinding<VariantContext> snpEffRodBinding = walker.getSnpEffRodBinding(); // Get only SnpEff records that start at this locus, not merely span it: List<VariantContext> snpEffRecords = tracker.getValues(snpEffRodBinding, ref.getLocus()); // Within this set, look for a SnpEff record whose ref/alt alleles match the record to annotate. // If there is more than one such record, we only need to pick the first one, since the // biological // effects will be the same across all such records: VariantContext matchingRecord = getMatchingSnpEffRecord(snpEffRecords, vc); if (matchingRecord == null) { return null; } // Parse the SnpEff INFO field annotation from the matching record into individual effect // objects: List<SnpEffEffect> effects = parseSnpEffRecord(matchingRecord); if (effects.isEmpty()) { return null; } // Add only annotations for one of the most biologically-significant effects from this set: SnpEffEffect mostSignificantEffect = getMostSignificantEffect(effects); return mostSignificantEffect.getAnnotations(); }