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

org.dishevelled.bio.variant.vcf.header.VcfHeaderLineParser Maven / Gradle / Ivy

The newest version!
/*

    dsh-bio-variant  Variants.
    Copyright (c) 2013-2024 held jointly by the individual authors.

    This library is free software; you can redistribute it and/or modify it
    under the terms of the GNU Lesser General Public License as published
    by the Free Software Foundation; either version 3 of the License, or (at
    your option) any later version.

    This library is distributed in the hope that it will be useful, but WITHOUT
    ANY WARRANTY; with out even the implied warranty of MERCHANTABILITY or
    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
    License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this library;  if not, write to the Free Software Foundation,
    Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA.

    > http://www.fsf.org/licensing/licenses/lgpl.html
    > http://www.opensource.org/licenses/lgpl-license.php

*/
package org.dishevelled.bio.variant.vcf.header;

import static com.google.common.base.Preconditions.checkNotNull;

import java.util.List;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.concurrent.Immutable;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ListMultimap;

/**
 * Parser for VCF header lines.
 *
 * @author  Michael Heuer
 */
@Immutable
final class VcfHeaderLineParser {

    /**
     * Return the required flag value for the specified key.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the required float value for the specified key
     */
    static Boolean requiredFlag(final String key, final ListMultimap entries) {
        return Boolean.valueOf(requiredString(key, entries));
    }

    /**
     * Return the required float value for the specified key.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the required float value for the specified key
     */
    static Float requiredFloat(final String key, final ListMultimap entries) {
        return Float.valueOf(requiredString(key, entries));
    }

    /**
     * Return the required integer value for the specified key.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the required integer value for the specified key
     */
    static Integer requiredInteger(final String key, final ListMultimap entries) {
        return Integer.valueOf(requiredString(key, entries));
    }

    /**
     * Return the required number value for the specified key.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the required number value for the specified key
     */
    static VcfHeaderLineNumber requiredNumber(final String key, final ListMultimap entries) {
        return VcfHeaderLineNumber.valueOf(requiredString(key, entries));
    }

    /**
     * Return the required type value for the specified key.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the required type value for the specified key
     */
    static VcfHeaderLineType requiredType(final String key, final ListMultimap entries) {
        return VcfHeaderLineType.valueOf(requiredString(key, entries));
    }

    /**
     * Return the required flag value for the specified key.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the required flag value for the specified key
     */
    static String requiredString(final String key, final ListMultimap entries) {
        checkNotNull(key);
        checkNotNull(entries);
        List values = entries.get(key);
        if (values.isEmpty()) {
            throw new IllegalArgumentException("required key " + key + " not found");
        }
        if (values.size() > 1) {
            throw new IllegalArgumentException("found more than one value for required key " + key);
        }
        return values.get(0);
    }

    /**
     * Return the optional flag value, if any.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the optional flag value, or null if no such value exists
     */
    static Boolean optionalFlag(final String key, final ListMultimap entries) {
        String value = optionalString(key, entries);
        return value == null ? null : Boolean.valueOf(value);
    }

    /**
     * Return the optional float value, if any.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the optional float value, or null if no such value exists
     */
    static Float optionalFloat(final String key, final ListMultimap entries) {
        String value = optionalString(key, entries);
        return value == null ? null : Float.valueOf(value);
    }

    /**
     * Return the optional integer value, if any.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the optional integer value, or null if no such value exists
     */
    static Integer optionalInteger(final String key, final ListMultimap entries) {
        String value = optionalString(key, entries);
        return value == null ? null : Integer.valueOf(value);
    }

    /**
     * Return the optional long value, if any.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the optional long value, or null if no such value exists
     */
    static Long optionalLong(final String key, final ListMultimap entries) {
        String value = optionalString(key, entries);
        return value == null ? null : Long.valueOf(value);
    }

    /**
     * Return the optional number value, if any.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the optional number value, or null if no such value exists
     */
    static VcfHeaderLineNumber optionalNumber(final String key, final ListMultimap entries) {
        String value = optionalString(key, entries);
        return value == null ? null : VcfHeaderLineNumber.valueOf(value);
    }

    /**
     * Return the optional type value, if any.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the optional type value, or null if no such value exists
     */
    static VcfHeaderLineType optionalType(final String key, final ListMultimap entries) {
        String value = optionalString(key, entries);
        return value == null ? null : VcfHeaderLineType.valueOf(value);
    }

    /**
     * Return the optional string value, if any.
     *
     * @param key key, must not be null
     * @param entries entries, must not be null
     * @return the optional string value, or null if no such value exists
     */
    static String optionalString(final String key, final ListMultimap entries) {
        checkNotNull(key);
        checkNotNull(entries);

        List values = entries.get(key);
        if (values.isEmpty()) {
            return null;
        }
        if (values.size() > 1) {
            throw new IllegalArgumentException("found more than one value for optional key " + key);
        }
        return values.get(0);
    }

    /** Pattern for structured header lines. */
    static final Pattern STRUCTURED = Pattern.compile("^##[a-zA-Z0-9_-]+=<.*ID=.+>$");

    /**
     * Return true if the specified line is a structured header line.
     *
     * @param line line, must not be null
     * @return true if the specified line is a structured header line
     */
    static boolean isStructured(final String line) {
        checkNotNull(line);
        Matcher m = STRUCTURED.matcher(line);
        return m.matches();
    }

// following method adapted from htsjdk/src/main/java/htsjdk/variant/vcf/VCFHeaderLineTranslator.java, and is licensed
/*
* Copyright (c) 2012 The Broad Institute
* 
* 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.
*/

    /**
     * Parse the specified VCF header line into a list multimap of key value entries.
     *
     * @param line line, must not be null
     * @return the specified VCF header line parsed into a list multimap of key value entries
     */
    static ListMultimap parseEntries(final String line) {
        checkNotNull(line);

        final ListMultimap entries = ArrayListMultimap.create();
        final StringBuilder builder = new StringBuilder();

        String key = "";
        int index = 0;
        boolean inQuote = false;
        boolean escape = false;

        for (char c: line.toCharArray()) {
            if (c == '\"') {
                if (escape) {
                    builder.append(c);
                    escape = false;
                }
                else {
                    inQuote = !inQuote;
                }
            }
            else if (inQuote) {
                if (escape) {
                    if (c == '\\') {
                        builder.append(c);
                    }
                    else {
                        builder.append('\\');
                        builder.append(c);
                    }
                    escape = false;
                }
                else if (c != '\\') {
                    builder.append(c);
                }
                else {
                    escape = true;
                }
            }
            else {
                escape = false;
                switch (c) {
                    case '<':
                        if (index != 0) {
                            builder.append(c);
                        }
                        break;
                    case '>':
                        if (index == line.length() - 1) {
                            entries.put(key, builder.toString());
                        }
                        break;
                    case '=':
                        key = builder.toString().trim();
                        builder.delete(0, builder.length());
                        break;
                    case ',':
                        entries.put(key, builder.toString());
                        builder.delete(0, builder.length());
                        break;
                    default:
                        builder.append(c);
                }
            }
            index++;
        }
        if (inQuote) {
            throw new RuntimeException("Unclosed quote in header line value " + line);
        }
        return entries;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy