nextflow.extension.Bolts.groovy Maven / Gradle / Ivy
/*
* Copyright (c) 2013-2015, Centre for Genomic Regulation (CRG).
* Copyright (c) 2013-2015, 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 groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import nextflow.file.FileHelper
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
/**
* 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 changing setting the delegate for each closure argument
for( int i=0; i findBestMatchesFor( 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
}
}