All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.fulcrumgenomics.vcf.api.Allele.scala Maven / Gradle / Ivy

The newest version!
/*
 * The MIT License
 *
 * Copyright (c) 2019 Fulcrum Genomics LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package com.fulcrumgenomics.vcf.api

import com.fulcrumgenomics.FgBioDef._
import htsjdk.samtools.util.SequenceUtil

/**
  * Represents an allele in a VCF or [[Variant]].  The only requirement of the trait is that implementing
  * classes override [[toString]] to provide the correct string representation of the allele.
  */
sealed trait Allele {
  /** Returns the displayed value of the allele as a String. */
  def value: String

  /** Ensures that the toString is always the same as the value. */
  override final def toString: String = value
}

object Allele {
  // Constant alleles for all the expected single-base alleles
  private val A = SimpleAllele("A")
  private val C = SimpleAllele("C")
  private val G = SimpleAllele("G")
  private val T = SimpleAllele("T")
  private val N = SimpleAllele("N")
  private val a = SimpleAllele("a")
  private val c = SimpleAllele("c")
  private val g = SimpleAllele("g")
  private val t = SimpleAllele("t")
  private val n = SimpleAllele("n")

  /**
    * Returns an allele for the string in question.  The string may be a string of bases containing
    * `[ACGTNacgtn]`, the no-call string (`.`), a spanned allele (`*`) or a symbolic or break-end allele.
    *
    * The allele that is returned may be newly created or cached.

    * @param value the String value of the allele
    * @return an instance of Allele that represents the given String
    */
  def apply(value: String): Allele = if (value.length == 1) singleCharAllele(value) else value match {
    case v if v.forall(ch => SequenceUtil.isUpperACGTN(ch.toUpper.toByte)) => SimpleAllele(v)
    case v if v.startsWith("<") && v.endsWith(">") => SymbolicAllele(v)
    case v if v.exists(c => c == '[' || c == ']')  => BreakendAllele(v)
    case v => throw new IllegalArgumentException(s"Oops, should probably handle allele: $v")
  }

  /** Optimized match-based lookup for expected single-base allele constants. */
  private def singleCharAllele(s: String): Allele = s.charAt(0) match {
    case 'A' => A
    case 'C' => C
    case 'G' => G
    case 'T' => T
    case 'N' => N
    case 'a' => a
    case 'c' => c
    case 'g' => g
    case 't' => t
    case 'n' => n
    case '.' => NoCallAllele
    case '*' => SpannedAllele
    case ch  => throw new IllegalArgumentException(s"Can't create an allele for '$ch'.")
  }

  /** Singleton representing an allele that is uncalled. */
  case object NoCallAllele extends Allele {
    override val value: String = "."
  }

  /** Singleton representing an allele that doesn't exist because it is spanned by an upstream allele. */
  case object SpannedAllele extends Allele  {
    override val value: String = "*"
  }

  /** Class for alleles composed of a simple string of bases. */
  case class SimpleAllele private(bases: String) extends Allele {
    def length: Int = bases.length
    override def value: String = bases

    /** Provides an implementation of equality based on case-insensitive matching of the bases. */
    override def equals(other: Any): Boolean = other.isInstanceOf[SimpleAllele] && {
      val that = other.asInstanceOf[SimpleAllele]
      (this eq that) || this.bases.equalsIgnoreCase(that.bases)
    }

    /** Generate the hash based on the upper-case version of the bases. */
    override def hashCode(): Int = this.bases.toUpperCase.hashCode
  }

  /** Class for symbolic alleles. */
  case class SymbolicAllele private (id: String) extends Allele {
    override def value: String = id
  }

  /** Class for break-end alleles. */
  case class BreakendAllele private (override val value: String) extends Allele // TODO: what does this need to have
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy