nextflow.extension.Bolts.groovy Maven / Gradle / Ivy
Show all versions of nf-commons Show documentation
/*
* Copyright 2013-2024, Seqera Labs
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package nextflow.extension
import java.nio.file.Path
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter
import java.util.concurrent.locks.Lock
import java.util.regex.Pattern
import groovy.transform.CompileDynamic
import groovy.transform.CompileStatic
import groovy.transform.Memoized
import groovy.transform.stc.ClosureParams
import groovy.transform.stc.FirstParam
import nextflow.file.FileHelper
import nextflow.file.FileMutex
import nextflow.util.CheckHelper
import nextflow.util.Duration
import nextflow.util.MemoryUnit
import nextflow.util.RateUnit
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 public final String DATETIME_FORMAT = 'dd-MM-yyyy HH:mm'
static private Pattern PATTERN_RIGHT_TRIM = ~/\s+$/
static private Pattern PATTERN_LEFT_TRIM = ~/^\s+/
@Memoized
static private ThreadLocal getLocalDateFormat(String fmt, TimeZone tz) {
return new ThreadLocal() {
@Override
protected DateFormat initialValue() {
def result = new SimpleDateFormat(fmt, Locale.ENGLISH)
if(tz) result.setTimeZone(tz)
return result
}
}
}
/**
* Format a {@link Date} object
*
* @param self The {@link Date} object to format
* @param format The date format to use eg. {@code dd-MM-yyyy HH:mm}.
* @param tz The timezone to be used eg. {@code UTC}. If {@code null} the current timezone is used.
* @return The date-time formatted as a string
*/
static String format(Date self, String format=null, String tz=null) {
TimeZone zone = tz ? TimeZone.getTimeZone(tz) : null
getLocalDateFormat(format ?: DATETIME_FORMAT, zone).get().format(self)
}
static String format(OffsetDateTime self, String format) {
return self.format(DateTimeFormatter.ofPattern(format).withLocale(Locale.ENGLISH))
}
/**
* Format a {@link Date} object
*
* @param self The {@link Date} object to format
* @param format The date format to use eg. {@code dd-MM-yyyy HH:mm}
* @param tz The timezone to be used. If {@code null} the current timezone is used.
* @return The date-time formatted as a string
*/
static String format(Date self, String format, TimeZone tz) {
getLocalDateFormat(format ?: DATETIME_FORMAT, tz).get().format(self)
}
static List pairs(Map self, Map opts=null) {
def flat = opts?.flat == true
def result = []
for( Map.Entry entry : self.entrySet() ) {
if( flat && entry.value instanceof Collection )
entry.value.iterator().each { result << [entry.key, it] }
else
result << [entry.key, entry.value]
}
return result
}
/**
* 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 String 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 String 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 String 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 org.apache.commons.lang.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 org.apache.commons.lang.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 ignored
* @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
}
}
/**
* Creates a file system wide lock that prevent two or more JVM instances/process
* to work on the same file
*
* Note: this does not protected against multiple-thread accessing the file in a
* concurrent manner.
*
* @param
* self The file over which define the lock
* @param
* timeout An option timeout elapsed which the a {@link InterruptedException} is thrown
* @param
* closure The action to apply during the lock file spawn
* @return
* The user provided {@code closure} result
*
* @throws
* InterruptedException if the lock cannot be acquired within the specified {@code timeout}
*/
static withLock(File self, Duration timeout = null, Closure closure) {
def locker = new FileMutex(self)
if( timeout )
locker.setTimeout(timeout)
locker.lock(closure)
}
/**
* Creates a file system wide lock that prevent two or more JVM instances/process
* to work on the same file
*
* Note: this does not protected against multiple-thread accessing the file in a
* concurrent manner.
*
* @param
* self The file over which define the lock
* @param
* timeout An option timeout elapsed which the a {@link InterruptedException} is thrown
* @param
* closure The action to apply during the lock file spawn
* @return
* The user provided {@code closure} result
*
* @throws
* InterruptedException if the lock cannot be acquired within the specified {@code timeout}
*/
static withLock( Path self, Duration timeout, Closure closure ) {
def locker = new FileMutex(self.toFile())
if( timeout )
locker.setTimeout(timeout)
locker.lock(closure)
}
static Duration toDuration(Number number) {
new Duration(number.toLong())
}
static MemoryUnit toMemory(Number number) {
new MemoryUnit(number.toLong())
}
/**
* Converts a {@code String} to a {@code Duration} object
*
* @param self
* @param type
* @return
*/
static Object asType( String self, Class