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

nextflow.extension.Bolts.groovy Maven / Gradle / Ivy

Go to download

A DSL modelled around the UNIX pipe concept, that simplifies writing parallel and scalable pipelines in a portable manner

There is a newer version: 18.12.0-edge
Show newest version
/*
 * Copyright (c) 2013-2016, Centre for Genomic Regulation (CRG).
 * Copyright (c) 2013-2016, Paolo Di Tommaso and the respective authors.
 *
 *   This file is part of 'Nextflow'.
 *
 *   Nextflow is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   Nextflow is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with Nextflow.  If not, see .
 */

package nextflow.extension

import java.nio.file.Path
import java.util.concurrent.locks.Lock
import java.util.concurrent.locks.ReentrantLock
import java.util.regex.Pattern

import com.google.common.cache.Cache
import com.google.common.cache.CacheBuilder
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import nextflow.file.FileHelper
import nextflow.util.CheckHelper
import nextflow.util.Duration
import org.apache.commons.lang.StringUtils
import org.codehaus.groovy.runtime.DefaultGroovyMethods
import org.codehaus.groovy.runtime.GStringImpl
import org.codehaus.groovy.runtime.ResourceGroovyMethods
import org.codehaus.groovy.runtime.StringGroovyMethods
import org.slf4j.Logger

/**
 * Generic extensions
 *
 * See more about extension methods
 * http://docs.codehaus.org/display/GROOVY/Creating+an+extension+module
 *
 * @author Paolo Di Tommaso 
 */
@CompileStatic
class Bolts {

    static private Pattern PATTERN_RIGHT_TRIM = ~/\s+$/

    static private Pattern PATTERN_LEFT_TRIM = ~/^\s+/

    /**
     * Remove the left side after a dot (including it) e.g.
     * 
     *     0.10     => 0
     *     10000.00 => 10000
     * 
* * @param self * @return */ static String trimDotZero(String self) { int p = self?.indexOf('.') p!=-1 ? self.substring(0,p) : self } /** * Remove blank chars at the end of the string * * @param self The string itself * @return The string with blanks removed */ static String rightTrim(String self) { self.replaceAll(PATTERN_RIGHT_TRIM,'') } /** * Remove blank chars at string beginning * * @param self The string itself * @return The string with blanks removed */ static String leftTrim( String self ) { self.replaceAll(PATTERN_LEFT_TRIM,'') } /** *

Strips any of a set of characters from the start and end of a String. * This is similar to {@link String#trim()} but allows the characters * to be stripped to be controlled.

* *

A null input String returns null. * An empty string ("") input returns the empty string.

* *

If the stripChars String is null, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}. * Alternatively use {@link #strip(String)}.

* *
     * StringUtils.strip(null, *)          = null
     * StringUtils.strip("", *)            = ""
     * StringUtils.strip("abc", null)      = "abc"
     * StringUtils.strip("  abc", null)    = "abc"
     * StringUtils.strip("abc  ", null)    = "abc"
     * StringUtils.strip(" abc ", null)    = "abc"
     * StringUtils.strip("  abcyx", "xyz") = "  abc"
     * 
* * @param str the String to remove characters from, may be null * @param stripChars the characters to remove, null treated as whitespace * @return the stripped String, null if null String input */ static strip( String self, String stripChars = null ) { StringUtils.strip(self, stripChars) } /** *

Strips any of a set of characters from the start of a String.

* *

A null input String returns null. * An empty string ("") input returns the empty string.

* *

If the stripChars String is null, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}.

* *
     * StringUtils.stripStart(null, *)          = null
     * StringUtils.stripStart("", *)            = ""
     * StringUtils.stripStart("abc", "")        = "abc"
     * StringUtils.stripStart("abc", null)      = "abc"
     * StringUtils.stripStart("  abc", null)    = "abc"
     * StringUtils.stripStart("abc  ", null)    = "abc  "
     * StringUtils.stripStart(" abc ", null)    = "abc "
     * StringUtils.stripStart("yxabc  ", "xyz") = "abc  "
     * 
* * @param str the String to remove characters from, may be null * @param stripChars the characters to remove, null treated as whitespace * @return the stripped String, null if null String input */ static stripStart( String self, String stripChars = null ) { StringUtils.stripStart(self, stripChars) } /** *

Strips any of a set of characters from the end of a String.

* *

A null input String returns null. * An empty string ("") input returns the empty string.

* *

If the stripChars String is null, whitespace is * stripped as defined by {@link Character#isWhitespace(char)}.

* *
     * StringUtils.stripEnd(null, *)          = null
     * StringUtils.stripEnd("", *)            = ""
     * StringUtils.stripEnd("abc", "")        = "abc"
     * StringUtils.stripEnd("abc", null)      = "abc"
     * StringUtils.stripEnd("  abc", null)    = "  abc"
     * StringUtils.stripEnd("abc  ", null)    = "abc"
     * StringUtils.stripEnd(" abc ", null)    = " abc"
     * StringUtils.stripEnd("  abcyx", "xyz") = "  abc"
     * StringUtils.stripEnd("120.00", ".0")   = "12"
     * 
* * @param str the String to remove characters from, may be null * @param stripChars the set of characters to remove, null treated as whitespace * @return the stripped String, null if null String input */ static stripEnd( String self, String stripChars = null ) { StringUtils.stripEnd(self, stripChars) } /** *

Capitalizes a String changing the first letter to title case as * per {@link Character#toTitleCase(char)}. No other letters are changed.

* *

For a word based algorithm, see {@link WordUtils#capitalize(String)}. * A null input String returns null.

* *
     * StringUtils.capitalize(null)  = null
     * StringUtils.capitalize("")    = ""
     * StringUtils.capitalize("cat") = "Cat"
     * StringUtils.capitalize("cAt") = "CAt"
     * 
* */ static String capitalize(String self) { StringUtils.capitalize(self) } /** *

Uncapitalizes a String changing the first letter to title case as * per {@link Character#toLowerCase(char)}. No other letters are changed.

* *

For a word based algorithm, see {@link WordUtils#uncapitalize(String)}. * A null input String returns null.

* *
     * StringUtils.uncapitalize(null)  = null
     * StringUtils.uncapitalize("")    = ""
     * StringUtils.uncapitalize("Cat") = "cat"
     * StringUtils.uncapitalize("CAT") = "cAT"
     * 
* */ static String uncapitalize(String self) { StringUtils.uncapitalize(self) } /** * Check if a alphabetic characters in a string are lowercase. Non alphabetic characters are ingored * @param self The string to check * @return {@true} if the string contains no uppercase characters, {@code false} otherwise */ static boolean isLowerCase(String self) { if( self ) for( int i=0; i T withLock( Lock self, boolean interruptible = false, Closure closure ) { // acquire the lock if( interruptible ) self.lockInterruptibly() else self.lock() try { return closure.call() } finally { self.unlock(); } } /** * Invokes the specify closure only if it is able to acquire a lock * * @param self * @param interruptible * @param closure * @return the closure result */ static boolean tryLock( Lock self, Closure closure ) { if( !self.tryLock() ) return false try { closure.call() } finally { self.unlock() return true } } /** * Converts a {@code String} to a {@code Duration} object * * @param self * @param type * @return */ static def asType( String self, Class type ) { if( type == Duration ) { return new Duration(self) } else if( Path.isAssignableFrom(type) ) { return FileHelper.asPath(self) } StringGroovyMethods.asType(self, type); } /** * Converts a {@code GString} to a {@code Duration} object * * @param self * @param type * @return */ static def asType( GString self, Class type ) { if( type == Duration ) { return new Duration(self.toString()) } else if( Path.isAssignableFrom(type) ) { return FileHelper.asPath(self) } StringGroovyMethods.asType(self, type); } /** * Converts a {@code Number} to a {@code Duration} object * * @param self * @param type * @return */ static def asType( Number self, Class type ) { if( type == Duration ) { return new Duration(self.longValue()) } DefaultGroovyMethods.asType(self, type); } /** * Converts a {@code File} to a {@code Path} object * * @param self * @param type * @return */ static def asType( File self, Class type ) { if( Path.isAssignableFrom(type) ) { return self.toPath() } ResourceGroovyMethods.asType(self, type); } private static Lock MAP_LOCK = new ReentrantLock() static def T getOrCreate( Map self, key, factory ) { if( self.containsKey(key) ) return (T)self.get(key) withLock(MAP_LOCK) { if( self.containsKey(key) ) return (T)self.get(key) def result = factory instanceof Closure ? factory.call(key) : factory self.put(key,result) return (T)result } } /** * Navigate a map of maps traversing multiple attribute using dot notation. For example: * {@code x.y.z } * * @param self The root map object * @param key A dot separated list of keys * @param closure An optional closure to be applied. Only if all keys exists * @return The value associated to the specified key(s) or null on first missing entry */ static def navigate( Map self, String key, Closure closure = null ) { assert key def items = key.split(/\./) def current = self.get(items[0]) for( int i=1; i def value = (config as Map).get(name) result.put(name, normalize0(value)) } return result } else if( config instanceof Collection ) { List result = new ArrayList(config.size()) for( entry in config ) { result << normalize0(entry) } return result } else if( config instanceof GString ) { return config.toString() } else { return config } } /** * Indent each line in the given test by a specified prefix * * @param text * @param prefix * @return The string indented */ public static String indent( String text, String prefix = ' ' ) { def result = new StringBuilder() def lines = text ? text.readLines() : Collections.emptyList() for( int i=0; i bestMatches( Collection options, String sample ) { assert sample assert options // Otherwise look for the most similar Map diffs = [:] options.each { diffs[it] = StringUtils.getLevenshteinDistance(sample, it) } // sort the Levenshtein Distance and get the fist entry def sorted = diffs.sort { Map.Entry it -> it.value } def nearest = (Map.Entry)sorted.find() def min = nearest.value def len = sample.length() def threshold = len<=3 ? 1 : ( len > 10 ? 5 : Math.floor(len/2)) List result if( min <= threshold ) { result = (List)sorted.findAll { it.value==min } .collect { it.key } } else { result = [] } return result } static boolean isCamelCase(String str) { if( !str ) return false for( int i=0; i T cloneWith( T self, binding ) { def copy = (T)self.clone() if( binding != null ) { copy.setDelegate(binding) copy.setResolveStrategy( Closure.DELEGATE_FIRST ) } return copy } /** * Create a copy of a {@link GString} object cloning all values that are instance of {@link Closure} * * @param self The {@link GString} itself * @param binding A {@link Map} object that is set as delegate object in the cloned closure. * @return The cloned {@link GString} instance */ static GString cloneWith( GString self, binding ) { def values = new Object[ self.valueCount ] // clone the gstring setting the delegate for each closure argument for( int i=0; i closest(Collection options, String sample ) { assert sample if( !options ) return Collections.emptyList() // Otherwise look for the most similar def diffs = [:] options.each { diffs[it] = StringUtils.getLevenshteinDistance(sample, it) } // sort the Levenshtein Distance and get the fist entry def sorted = diffs.sort { it.value } def nearest = sorted.find() def min = nearest.value def len = sample.length() def threshold = len<=3 ? 1 : ( len > 10 ? 5 : Math.floor(len/2)) def result if( min <= threshold ) { result = sorted.findAll { it.value==min } .collect { it.key } } else { result = [] } return result } private static HashMap LOGGER_CACHE = new LinkedHashMap() { protected boolean removeEldestEntry(Map.Entry eldest) { return size() > 10_000 } } private static final Duration LOG_DFLT_THROTTLE = Duration.of('1min') static private checkLogCache( Object msg, Map params, Closure action ) { // -- check if this message has already been printed final String str = msg.toString() final Throwable error = params?.causedBy as Throwable final Duration throttle = params?.throttle as Duration ?: LOG_DFLT_THROTTLE long now = System.currentTimeMillis() Long ts = LOGGER_CACHE.get(str) if( ts && now - ts <= throttle.toMillis() ) { return } LOGGER_CACHE.put(str, now) action.call(str, error) } private static Map LOGGER_PARAMS = [ causedBy: Throwable, throttle: [String, Number, Duration] ] /** * Append a `trace` level entry in the application log. * * @param log * The {@link Logger} object * @param params * Optional named parameters * - `causedBy`: A {@link Throwable} object that raised the error * - `throttle`: When specified suppress identical logs within the specified time {@link Duration} * @param msg * The message to print * */ static void trace1(Logger log, Map params=null, Object msg ) { CheckHelper.checkParams('trace1', params, LOGGER_PARAMS) if( !log.isTraceEnabled() || msg==null ) return checkLogCache(msg,params) { String str, Throwable t -> t ? log.trace(str,t) : log.trace(str) } } /** * Append a `debug` level entry in the application log. * * @param log * The {@link Logger} object * @param params * Optional named parameters * - `causedBy`: A {@link Throwable} object that raised the error * - `throttle`: When specified suppress identical logs within the specified time {@link Duration} * @param msg * The message to print * */ static void debug1(Logger log, Map params=null, Object msg ) { CheckHelper.checkParams('debug1', params, LOGGER_PARAMS) if( !log.isDebugEnabled() || msg==null ) return checkLogCache(msg,params) { String str, Throwable t -> t ? log.debug(str,t) : log.debug(str) } } /** * Append a `info` level entry in the application log. * * @param log * The {@link Logger} object * @param params * Optional named parameters * - `causedBy`: A {@link Throwable} object that raised the error * - `throttle`: When specified suppress identical logs within the specified time {@link Duration} * @param msg * The message to print * */ static void info1(Logger log, Map params=null, Object msg ) { CheckHelper.checkParams('info1', params, LOGGER_PARAMS) if( !log.isInfoEnabled() || msg==null ) return checkLogCache(msg,params) { String str, Throwable t -> t ? log.info(str,t) : log.info(str) } } /** * Append a `warn` level entry in the application log. * * @param log * The {@link Logger} object * @param params * Optional named parameters * - `causedBy`: A {@link Throwable} object that raised the error * - `throttle`: When specified suppress identical logs within the specified time {@link Duration} * @param msg * The message to print * */ static void warn1(Logger log, Map params=null, Object msg ) { CheckHelper.checkParams('warn1', params, LOGGER_PARAMS) if( !log.isWarnEnabled() || msg==null ) return checkLogCache(msg,params) { String str, Throwable t -> t ? log.warn(str,t) : log.warn(str) } } /** * Append a `error` level entry in the application log. * * @param log * The {@link Logger} object * @param params * Optional named parameters * - `causedBy`: A {@link Throwable} object that raised the error * - `throttle`: When specified suppress identical logs within the specified time {@link Duration} * @param msg * The message to print * */ static void error1(Logger log, Map params=null, Object msg ) { CheckHelper.checkParams('error1', params, LOGGER_PARAMS) if( !log.isErrorEnabled() || msg==null ) return checkLogCache(msg,params) { String str, Throwable t -> t ? log.error(str,t) : log.error(str) } } static void trace(Logger log, Object msg) { if( log.isTraceEnabled() ) { log.trace(msg.toString()) } } static void trace(Logger log, Object msg, Throwable e) { if( log.isTraceEnabled() ) { log.trace(msg.toString(),e) } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy