• Stars
    star
    71
  • Rank 443,012 (Top 9 %)
  • Language
    Groovy
  • License
    Other
  • Created about 12 years ago
  • Updated over 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

groovy-common-extensions

Build Status

Lets you add things I find commonly useful to the Groovy language via the extension system.

Obviously requires at least Groovy 2.0.5 (so that the extension system exists)

Usage:

@Grab( 'com.bloidonia:groovy-common-extensions:0.7.1' )

and the following methods will be available to you:

Current extensions:

addImplicitConversion (thanks MartyNeal)

static void addImplicitConversion(Class self, Class to, Closure conversion)

Lets you overload the asType call in Groovy, so you can do:

String.addImplicitConversion(Integer) { it.length() }
assert ("foo" as Integer) == 3

clamp

static <T extends Comparable> T clamp( T self, T lower, T upper )
static <T extends Comparable> T clamp( T self, Range range ) {
static Range clamp( Range self, Range range ) {

Lets you do:

println 10.clamp(  1, 15 )    // 10
println 10.clamp(  1,  5 )    // 5
println 10.clamp( 12, 20 )    // 12
println 10.clamp( 12..20 )    // 12
println (3..20).clamp( 2..9 ) // 3..9

Works with any comparable:

println 'a'.clamp( 'b', 'z' ) // b

Multi closure sort

static Collection sort( Collection self, boolean mutate, Closure... closures )

Lets you do (example from here):

List list = [
  [id:0, firstName: 'Sachin', lastName: 'Tendulkar', age: 40 ],
  [id:1, firstName: 'Sachin', lastName: 'Tendulkar', age: 103 ],
  [id:2, firstName: 'Ajay', lastName: 'Tendulkar', age: 48 ],
  [id:3, firstName: 'Virendra', lastName: 'Sehwag', age: 5 ],
  [id:4, firstName: 'Virendra', lastName: 'Sehwag', age: 50 ],
  [id:5, firstName: 'Sachin', lastName: 'Nayyar', age: 15 ]
]

// returns [2, 5, 0, 1, 3, 4]
list.sort( false, { it.firstName }, { it.lastName }, { it.age } )*.id

Negative index take (with Lists)

static List take( List self, int n )

Lets you safely grab the end of a list, as you can with take for the front

println [1,2,3,4].take( -2 ) // [3,4]

If you pass a positive number, it delegates to the original DGM.take method

println [1,2,3,4].take( 2 ) // [1,2]

withClosable

static Object withClosable( Object self, Closure c ) {

Executes the closure (passing the delegate), and when finished it calls close on the delegate if the method exists. If it doesn't exist, it does nothing.

Examples:

// Create a FileWriter, close it when finished
new FileWriter( '/tmp/d.txt' ).withClosable {
  it.println 'd'
}

// Create 3 FileWriters, close them all when done, and return
// Closure result ('tim') to the rslt var
def rslt = [ '/tmp/a.txt', '/tmp/b.txt', '/tmp/c.txt' ].collect {
  new FileWriter( it )
}.withClosable { a, b, c ->
  a.println 'a'
  b.println 'b'
  c.println 'c'
  'tim'
}
assert rslt == 'tim'

// This works, but does nothing
'tim'.withClosable {
  println it
}

tap

static Object tap( Object self, Closure c ) {

Taken from Ruby, the tap method executes the closure using the object as the delegate - internally, it just calls self.with c and then it returns self.

This allows you to tap into a method chain:

def m = (1..10)                         .tap { println "original ${it}" }
               .findAll { it % 2 == 0 } .tap { println "evens    ${it}" }
               .collect { it * it }     .tap { println "squares  ${it}" }
// prints:
//    original [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
//    evens    [2, 4, 6, 8, 10]
//    squares  [4, 16, 36, 64, 100]

// and returns:
assert m == [4, 16, 36, 64, 100]

zip and unzip

static File zip ( File self )
static File zip ( File self, File destination )
static File zip ( File self, Closure<Boolean> filter )
static File zip ( File self, File destination, Closure<Boolean> filter )

static Collection<File> unzip ( File self )
static Collection<File> unzip ( File self, File destination )
static Collection<File> unzip ( File self, Closure<Boolean> filter )
static Collection<File> unzip ( File self, File destination, Closure<Boolean> filter )

Zips/unzips a single file or directory tree. If no destination is given, the target directory for the generated zip or extracted file(s) will be relative to the current file location. If a filter is specified, then each file is passed to the closure before it is handled. If the closure returns false, the file is skipped.

Examples:

// zips the directory tree and creates a 'tmp.zip' file in '/var'
File zipFile = new File('/var/tmp/').zip()

// zips all *.txt files in the directory tree and creates a 'tmp.zip' file in '/var'
File zipFile = new File('/var/tmp/').zip {
  it.name.endsWith '.txt'
}

// extracts the files to '/var/'
Collection<File> extractedFiles = new File('/var/tmp.zip').unzip()

// zips the directory content and moves 'tmp.zip' to '/home/bill'
File zipFile = new File('/var/tmp/').zip(new File('/home/bill/tmp.zip')

// extracts the files to '/home/bill/'
Collection<File> extractedFiles = new File('/var/tmp.zip').unzip(new File('/home/bill'))

toMap functionality for NodeChild

static Map toMap( NodeChild self )
static Map toMap( NodeChild self, String childKey )

Converts a NodeChild and its children to a Map. Child nodes are by default put inside a key _children, though this can be changed using the optional childKey parameter (if your xml contains _children nodes for example)

Examples:

def xmlstr = '<dan value="a"><subnode><item value="a"/></subnode></dan>'
def xml = new XmlSlurper().parseText( xmlstr )
def map = xml.toMap()

assert map == [dan:[value:'a',_children:[[subnode:[_children:[[item:[value:'a']]]]]]]]

And with a childKey:

def xmlstr = '<dan value="a"><subnode><item value="a"/></subnode></dan>'
def xml = new XmlSlurper().parseText( xmlstr )
def map = xml.toMap( 'kids' )

assert map == [dan:[value:'a',kids:[[subnode:[kids:[[item:[value:'a']]]]]]]]

rand functionality for List and Iterator

static <T> T rand( List<T> self )
static <T> List<T> rand( List<T> self, int n ) {
static <T> List<T> rand( List<T> self, int n, boolean allowDuplicates ) {
static <T> List<T> rand( List<T> self, int n, boolean allowDuplicates, Random r ) {

static <T> T rand( Iterator<T> self )
static <T> List<T> rand( Iterator<T> self, int n )
static <T> List<T> rand( Iterator<T> self, int n, boolean allowDuplicates )
static <T> List<T> rand( Iterator<T> self, int n, boolean allowDuplicates, Random r )

Randomly select an element from a list or iterator.

  • The first form returns a single random element from the List or Iterator.
  • The second form returns a List of n random elements from the List or Iterator (duplicates allowed)
  • The third form allows you to specify no duplicates (by passing false as the third parameter) and returns a List
  • The fourth form also returns a List and also allows you to set the Random object to be used in the processing. This allows tests to specify a seed so reproducability is assured.

The returned list is of the same class as the input.

If you ask for 0 items, you get an empty list returned.

If you ask for more unique elements than there are items in the list, this throws an IllegalArgumentException

When rand is called on an Iterator, the entire Iterator is consumed until hasNext() is false and the returned elements are chosen from all the items iterated over with uniform probability.

Example:

def list = [1, 2, 3, 4, 5]
def randomInt = list.rand()
assert randomInt in list

Extended merge functionality for ConfigObject

static ConfigObject merge( Map self, Map other, Boolean sourcePrecedence )

Boolean sourcePrecedence specifies that the source ConfigObject should not have it's existing key/value pair overwritten by a merge with another ConfigObject

Example:

    def config1 = """
        config {
            a = 1
            b = 2
            c = 3
        }
    """
    def config2 = """
        config {
            a = 2
            b = 2
        }
    """

    def configObject1 = new ConfigSlurper().parse(config1)
    def configObject2 = new ConfigSlurper().parse(config2)
    def merge = configObject2.merge(configObject1)

    def configObject3 = new ConfigSlurper().parse(config1)
    def configObject4 = new ConfigSlurper().parse(config2)
    def mergeWithSourcePrecedence = configObject4.merge(configObject3,true)

    // overwrote config2's value
    assert merge.config.a == 1
    // preserved config2's value
    assert mergeWithSourcePrecedence.config.a == 2

    // config2 inherited value from config1
    assert merge.config.c == 3
    // config2 inherited from config1
    assert mergeWithSourcePrecedence.config.c == merge.config.c

Transposed Iterator for List of Lists

static <T> Iterator<T> transposedIterator( List<List<T>> lists )
static <T> Iterator<T> transposedIterator( List<List<T>> lists, List<Integer> amounts )

Given a list of lists, ie:

def a = [ 1..4, 'a'..'e' ]

you can (with Groovy) call a.transpose() ie:

assert a.transpose() == [ [ 1,'a' ], [ 2,'b' ], [ 3,'c' ], [ 4,'d' ] ]

This new functionality returns an iterator, and you can specify how many reseults of each list to expect. So:

// We need the collect as we want the values from the Iterator
def b = a.transposedIterator().collect()

// Note we get the extraneous 'e' that is lost by transpose()
assert b == [ 1, 'a', 2, 'b', 3, 'c', 4, 'd', 'e' ]

And (passing a list of amounts):

// So we want 1 element of list 1 followed by 2 of list 2 (repeated till exhaustion)
def c = a.transposedIterator( [ 1, 2 ] ).collect()

// Note we run out of list 2 after the 'e', so just get the last 4 from list 1
assert c == [ 1, 'a', 'b', 2, 'c', 'd', 3, 'e', 4 ]

Averages for collection of Numbers

static <V extends Number> AverageStats<V> average( Collection<V> collection )

Given a collection of Numbers, ie:

def a = 1..10

We can get the mean, median, variance and stdDev wrapped in an Immutable AverageStats class by simply calling:

def stats = a.average()

assert stats.mean     == 5.5
assert stats.median   == 5.5
assert stats.variance == 8.25
assert String.format( '%.5f', stats.stdDev ) == '2.87228'

This is useful when we are assuming a collection of numbers is the same based on it's superficial criteria, ie given:

def a = [1] * 50 + [100] * 50
def b = 1..100

It is easy using simple (bogus) stats to say they are very similar:

// Both print: "MIN 1 MAX 100 SIZE 100 MEAN 50.5"
println "MIN ${a.min()} MAX ${a.max()} SIZE ${a.size()} MEAN ${a.sum() / 100}"
println "MIN ${b.min()} MAX ${b.max()} SIZE ${b.size()} MEAN ${b.sum() / 100}"

However, the standard deviation shows that they are very different sequences of numbers:

// prints: "AverageStats( mean:50.5, median:50.5, variance:2450.25, stdDev:49.5 )"
println a.average()
// prints: "AverageStats( mean:50.5, median:50.5, variance:833.25, stdDev:28.86607004772212 )"
println b.average()

byte[].hexdump()

static void hexdump( byte[] self, Writer writer, int idx, int len )
static void hexdump( byte[] self, int idx, int len )
static void hexdump( byte[] self, Writer writer, int idx )
static void hexdump( byte[] self, int idx )
static void hexdump( byte[] self, Writer writer )
static void hexdump( byte[] self )

Dumps a byte array out as hex in a readable form. You can also pass a Writer to print to this rather than System.out (the default)

ie: calling:

String output = new StringWriter().with { w ->
    'Hello and welcome to Groovy'.bytes.hexdump( w )
    w.toString()
}

Writes the following to output:

           +--------------------------------------------------+
           | 0  1  2  3  4  5  6  7   8  9  a  b  c  d  e  f  |
+----------+--------------------------------------------------+------------------+
| 00000000 | 48 65 6c 6c 6f 20 61 6e  64 20 77 65 6c 63 6f 6d | Hello and welcom |
| 00000010 | 65 20 74 6f 20 47 72 6f  6f 76 79                | e to Groovy      |
+----------+--------------------------------------------------+------------------+

String.toXml

static GPathResult toXml( String self ) {
static GPathResult toXml( String self, boolean validating, boolean namespaceAware)
static GPathResult toXml( String self, boolean validating, boolean namespaceAware, boolean allowDocTypeDeclaration)
static GPathResult toXml( String self, SAXParser parser)
static GPathResult toXml( String self, XMLReader reader)

Will pass any String through XmlSlurper with the specified constructor parameters, ie:

assert '<xml><name>Tim</name></xml>'.toXml().name == 'Tim'

String.toConfig

static ConfigObject toConfig( String self ) {

Example: assert config1.toConfig().config.a==1

String.toJson

static Object toJson( String self ) {

Example: assert '{"xml":{"name":"Tim"}}'.toJson().xml.name == 'Tim'

More Repositories

1

groovy-stream

A collection of classes to give a fluent builder for Streams (Lazy Groovy Generators)
Java
66
star
2

mod-jdbc-persistor

BusMod Persistor for JDBC support in vert.x
Java
36
star
3

mod-metrics

A simple metrics mod for vert.x
Java
30
star
4

ShadowFX

Java
15
star
5

minimal-groovy-storm-starter

A starting point for Storm distributed processing with Groovy and Gradle
Groovy
12
star
6

groovy-logic

Groovy
10
star
7

groovy-debug-transform

An AST transformation to print debug info about each line as it's executed
Groovy
9
star
8

GradientExtractorFX

Extract a JavaFX CSS gradient from an image
Java
7
star
9

spock-ignoreuntil

Adds an annotation to Spock tests to ignore a feature or specification until a given date up to 30 days in the future.
Groovy
5
star
10

EnsemblRest

R package for accessing the Ensembl REST api
R
4
star
11

ascii-image

Java
4
star
12

vertx-examples

Some examples and tests of the vert.x framework
JavaScript
3
star
13

vertx-example-jdbc-webapp

The example webapp from the vert.x examples changed to run with the mod-jdbc-persistor
CSS
3
star
14

RaspberryGreet

Ask for name and print "Hello Name" in multiple languages (with Raspberry Pi install instructions for each language)
Assembly
3
star
15

rbioc2011

R
3
star
16

GroovyCGradle

A Gradle 1.9 project that has Groovy code calling C code
Shell
2
star
17

data-gorm-mysql

Spring Boot, GORM, Hibernate and MySQL
Shell
2
star
18

mod-circuit-breaker

A WIP Circuit Breaker patter for Vert.x
Java
2
star
19

gradle-dependencies

Groovy
1
star
20

akka-kata-groovy

A Groovy/Gradle version of https://github.com/henrikengstrom/akka-kata-java
Groovy
1
star
21

simplest-proj

Pretty much the simplest Vert.x Maven project
Groovy
1
star
22

advent-of-kotlin-2021

See how far I get this year https://adventofcode.com/2021
Kotlin
1
star
23

ratpack-scheduler

A Ratpack App with Sundial for scheduling
Java
1
star
24

bloidonia-repo

Maven repository for my stuff
1
star
25

intellij-rest-linkomatic

Copy a REST link that will open a file in intellij
Kotlin
1
star
26

groovy-script-griffon

Griffon wrapper for stuff
Groovy
1
star