Thursday, December 29, 2011

ASCII Art

/**
 * Groovy ASCII Art. Converts an image into ASCII.
 * This doesn't work under the web console due to missing AWT classes.
 *
 * Author  : Cedric Champeau (http://twitter.com/CedricChampeau)
 * Updated : Steven Olsen (http://crazy4groovy.blogspot.com)
 */
import java.awt.color.ColorSpace as CS
import java.awt.geom.AffineTransform
import javax.imageio.ImageIO
import java.awt.image.*

String nl = System.getProperty("line.separator")
String slash = System.getProperty("file.separator")
def input = System.console().&readLine

def charset1 = /#@$&%*o=^|-:,'. / //16 chars
//def charset2 = /#@$&%*o=^|-:,'. / //16 chars
def charset2 = /ABCDEFGHIJKLMNOP/ //16 chars

/////////CLI///////////
def cli = new CliBuilder(usage:'asciiArt [options] [path/file/url]', header:'Options:')
cli.h    (longOpt:'help', 'print this message')
cli.bw    (longOpt:'blackWhiteText', 'set normal black/white text')
cli.ctxt(longOpt:'colourText', 'set html colour (text)')
cli.cbg    (longOpt:'colourBackground', 'set html colour (background)')
cli.ics    (longOpt:'isCharSequ', 'output char map in sequence')
cli.vf    (longOpt:'verifyFile', 'verify each file write with a confirmation message')
cli.r    (longOpt:'recursiveFiles', 'recursively iterate through all files in a directory')
cli.cm    (longOpt:'characterMapping', args:1, argName:'percent', 'set custom char map (16)')
cli.s    (longOpt:'scale', args:1, argName:'percentage', 'scale image resolution for output processing (default=40)')
cli.ext    (longOpt:'fileExtension', args:1, argName:'ext', 'name of file extension (default=txt or html)')
cli.incl(longOpt:'fileExtensionInclude', args:1, argName:'regex', 'regex -- name of file extensions to include (default=jpe?g|png|gif)')
cli.outDir    (longOpt:'outputDir', args:1, argName:'...\\dir\\', 'output into dir')
cli.outFile    (longOpt:'outputFile', args:1, argName:'...\\file', 'output into file')

def opt = cli.parse(args)

if (opt.h) {
    cli.usage(); return
}
/////////CLI///////////

List srcs
if (opt.arguments().size() >= 1)
    srcs = opt.arguments()
else
    srcs = [input('image file (local or http): ')] ?: ['http://gordondickens.com/images/groovy.png']

srcs = srcs.collect{ it.replaceAll('"','').split(',') }.flatten()

Set imgSrcList = [] as SortedSet
String filter = opt.incl ?: 'jpe?g|png|gif'
imgSrcList.metaClass.leftShift { if (it.split('\\.')[-1] ==~ "(${filter})") { delegate.add it } }

srcs.each { s ->
    boolean isRemote = s ==~ 'https?://.*'
    if (isRemote) {
        imgSrcList.add s // bypass meatclass filter with .add
        return
    }
    
    def f = new File(s)
    if (!f.directory == true && f.name.split('\\.')[-1] ==~ '(jpe?g|png)') {
        imgSrcList << s
        return
    }
    
    if (opt.r)
        f.eachFileRecurse { fi -> 
            imgSrcList << fi.path }
    else
        f.eachFile { fi -> 
            imgSrcList << fi.path }
}

boolean isMultiImg = (imgSrcList.size() > 1)

Boolean isCharSequGlobal = null // once set to true/false, val will always be used
imgSrcList.eachWithIndex { src, i ->
try {

println "** IMAGE ${i+1} of ${imgSrcList.size()}: $src ..."

boolean isRemote = src ==~ 'https?://.*'
def imgSrc = ImageIO.read( isRemote ? new URL(src) : new File(src))

def scale = opt.s ? opt.s.toBigDecimal() / 100 : 0.4G
String fileName = opt.outFile ?: opt.outDir ? '' : 'SCREEN'

boolean convert = true
while (convert) {
    ////////////INPUT START////////////
    scale *= 100 // reset scale to %
    scale = isMultiImg ? scale : (input("scale of ascii art (percentage) [${scale}]: ") ?: scale)
    scale = scale.toBigDecimal() / 100 // prep scale for usage
    boolean isHtmlColour = opt.bw ? false : (opt.ctxt ?: opt.cbg ?: (input('html colour chars? [y/N]: ').toLowerCase().contains('y') ? true : false) )
    boolean isBgColour = false
    if (isHtmlColour)
        isBgColour = opt.cbg ?: isMultiImg ? false : (input('set background colour? [y/N]: ').toLowerCase().contains('y') ? true : false)
    String charsMapping = opt.cm ?: isMultiImg ? '' : (input('custom char set? (16): ') ?: null)
    if (isHtmlColour && charsMapping && charsMapping.size() < 16 && !isMultiImg)
        println "WARNING: custom char set size is less than 16 (${charsMapping.size()})\n -- must be output in order!"
    boolean isCharSequ = isCharSequGlobal ?: false
    if (isHtmlColour && charsMapping && charsMapping.size() >= 1) {
        isCharSequ = opt.ics ?: isCharSequGlobal ?: (input('output custom chars in order? [Y/n]:').toLowerCase().contains('n') ? false : true)
        isCharSequGlobal = isCharSequ
    }
    if (isHtmlColour && charsMapping && charsMapping.size() < 16 && !isCharSequ)
        println "ERROR: custom char set REJECTED -- size is less than 16 (${charsMapping.size()}) and output is not ordered.\n -- Using default char set."
    if (!isHtmlColour && charsMapping && charsMapping.size() < 16)
        if (i == 0) println "ERROR: non-colour custom char set REJECTED -- size is less than 16 (${charsMapping.size()})\n -- Using default char set."
    if (!isCharSequ && charsMapping?.size() < 16)
        charsMapping = isHtmlColour ? charset2 : charset1
    
    fileName = opt.outFile ?: opt.outDir ? '' : isMultiImg ? '' : input("save ascii art into File (SCREEN = print to screen) [${fileName}]: ") ?: fileName
    ////////////INPUT END////////////

    def yScaleOffset = isHtmlColour ? 0.7 : 0.6 // ascii imgs looked too "tall" -- dev tweakable!
    def cSpace = isHtmlColour ? CS.CS_sRGB : CS.CS_GRAY

    ////////GENERATE////////
    def img = imgSrc
    if (scale != 1.0) {
        def tx = new AffineTransform()
        tx.scale(scale, scale * yScaleOffset)
        def op = new AffineTransformOp(tx, AffineTransformOp.TYPE_BILINEAR)
        def scaled = new BufferedImage((int) (imgSrc.width * scale), (int) (imgSrc.height * scale * yScaleOffset), imgSrc.type)
        img = op.filter(imgSrc, scaled)
    }

    img = new ColorConvertOp(CS.getInstance(cSpace), null).filter(img, null)

    BigInteger pixelCntr = 0G
    def ascii = { rgb ->
        int r = (rgb & 0x00FF0000) >> 16
        int g = (rgb & 0x0000FF00) >> 8
        int b = (rgb & 0x000000FF)

        int gs
        if (isCharSequ)  gs = (pixelCntr++) % charsMapping.size()
        else  gs = ((int) ( r + g + b ) / 3) >> 4 // multiple of 16

        return [ charsMapping.charAt(gs), [r,g,b] ]
    }

    String preStyle = " style='opacity:1.0;font-size:0.8em;line-height:85%;${ isBgColour ? 'color:#FFF;' : '' }'"
    String spanStyle = isBgColour ? "background-color" : "color"

    StringBuilder sb = new StringBuilder()
    if(isHtmlColour) sb.append("<style>pre img{opacity:0.0;border:4px dotted #444} pre img:hover{opacity:0.95;}</style>"+nl+
        "<pre${preStyle}>"+nl+
        "<div style='position:absolute'><img src='${!isRemote ? 'file://' : ''}${src}' style='position:absolute;top:5px;left:5px;'/></div>"+nl)
    img.height.times { y ->
        img.width.times { x ->
            (chr, rgb) = ascii(img.getRGB(x, y))
            if (isBgColour || (isHtmlColour && chr != ' '))
                sb.append("<span style='${spanStyle}:rgb(${rgb.join(',')});'>${chr}</span>")
            else
                sb.append(chr)
        }
        sb.append(nl)
    }
    if(isHtmlColour) sb.append("</pre>"+(nl * 2))
    ////////GENERATE////////

    if (fileName == 'SCREEN') {
        println sb.toString()
    }
    else {
        if (!fileName) {
            File f = new File(src) // to get file name and parent fields
            String fExt = opt.outFile ? '' : '.ascii'
            fExt += opt.outFile ? '' : ('.' + (opt.ext ?: isHtmlColour ? 'html' : 'txt'))
            fileName =  (opt.outDir ?: f.parent) + slash + f.name + fExt
        }

        boolean ok = !opt.vf ?: input("WARNING: writing to file ${fileName} ok? [Y/n]").toLowerCase().contains('n') ? false : true
        File f = new File(fileName)
        if (ok) {
            f << sb.toString()
            println "\t>> ${f.name}"
        }
    }

    convert = isMultiImg ? false : (input('>> export this image to ascii format again? [y/N]: ').toLowerCase().contains('y') ? true : false)
    if (convert) println '=' * 40
}

} catch (Exception e) { println "\tERROR: ${e.toString()}"}
}


gist link

Thursday, December 15, 2011

rabbitMQ - Publish/Subscribe

@Grab('com.rabbitmq:amqp-client:2.7.0')
import com.rabbitmq.client.*

public class SendReceive {
  private String QUEUE
  private Channel channel
  
  public SendReceive( String hostName = "localhost", String queue = "hello" ) {
        QUEUE = queue
        ConnectionFactory factory = new ConnectionFactory()
//      factory.setUsername(userName)
//      factory.setPassword(password)
//      factory.setVirtualHost(virtualHost)
        factory.setHost(hostName)
//      factory.setPort(portNumber)
        Connection connection = factory.newConnection()
        channel = connection.createChannel()
        channel.exchangeDeclare(QUEUE, "fanout")
  }

  public void send( String message )
      throws java.io.IOException {
        channel.basicPublish(QUEUE, "", null, message.getBytes())
        println(" [x] Sent: $message")
        
        //channel.close()
        //connection.close()
  }

  public void receive()
      throws java.io.IOException, java.lang.InterruptedException {
        String queueName = channel.queueDeclare().getQueue();
        channel.queueBind(queueName, QUEUE, "");
        
        QueueingConsumer consumer = new QueueingConsumer(channel)
        channel.basicConsume(queueName, true, consumer)
        println(" [*] Waiting for messages. To exit press CTRL+C")

        while (true) { //loop forever!
            QueueingConsumer.Delivery delivery = consumer.nextDelivery() // blocks for next message!
            String message = new String(delivery.getBody())
            println(" [x] Received: $message")
        }
    }
}

def input = System.console().&readLine // convenience method

def client = new SendReceive(
    input('\nEnter rabbitMQ host location: '),
    input('\nEnter rabbitMQ queue name: ')
)

String userType = input('\n[s]end or [r]eceive?: ')

if (userType && userType == 'r') {
    client.receive()
}
else {
    while(true) {
        String msg = input('\nType a message to send -- To exit press CTRL+C: ')
        client.send(msg)
    }
}

rabbitMQ - Queue

@Grab('com.rabbitmq:amqp-client:2.7.0')
import com.rabbitmq.client.*

public class SendReceive {
  private String QUEUE
  private Channel channel
  
  public SendReceive( String hostName = "localhost", String queue = "hello" ) {
        QUEUE = queue
        ConnectionFactory factory = new ConnectionFactory()
//      factory.setUsername(userName)
//      factory.setPassword(password)
//      factory.setVirtualHost(virtualHost)
        factory.setHost(hostName)
//      factory.setPort(portNumber)
        Connection connection = factory.newConnection()
        channel = connection.createChannel()
        channel.queueDeclare(QUEUE, false, false, false, null)
  }

  public void send( String message )
      throws java.io.IOException {
        channel.basicPublish("", QUEUE, null, message.getBytes())
        println(" [x] Sent: $message")
        
        //channel.close()
        //connection.close()
  }

  public void receive()
      throws java.io.IOException, java.lang.InterruptedException {

        QueueingConsumer consumer = new QueueingConsumer(channel)
        channel.basicConsume(QUEUE, true, consumer)
        println(" [*] Waiting for messages. To exit press CTRL+C")

        while (true) { //loop forever!
            QueueingConsumer.Delivery delivery = consumer.nextDelivery() // blocks for next message!
            String message = new String(delivery.getBody())
            println(" [x] Received: $message")
        }
    }
}

def input = System.console().&readLine // convenience method

def client = new SendReceive(
    input('\nEnter rabbitMQ host location: '),
    input('\nEnter rabbitMQ queue name: ')
)

String userType = input('\n[s]end or [r]eceive?: ')

if (userType && userType == 'r') {
    client.receive()
}
else {
    while(true) {
        String msg = input('\nType a message to send -- To exit press CTRL+C: ')
        client.send(msg)
    }
}

Multi-Assign, Transform Map Keys

(a,b,c) = [1,2]
assert a == 1
assert c == null

(a,b,c) = {[1,2]}()
assert a == 1
assert c == null

Map o = [a:1,b:2,c:3,d:4,aa:5]
// the objective it to keep only the keys with 'a', and uppercase them //

Map f1 = [:]
o.findAll { i -> i.key.contains('a') }
    .each { i -> f1[i.key.toUpperCase()] = i.value }
println f1 // [A:1, AA:5]

Map f2 = [:]
o.each { i ->
    if (i.key.contains('a'))
        f2[i.key.toUpperCase()] = i.value 
}
println f2 // [A:1, AA:5]

Map g1 = o
    .findAll { i -> i.key.contains('a') }
    .inject([:]) { m, i ->
        m[i.key.toUpperCase()] = i.value
        return m
    }
println g1 // [A:1, AA:5]

Map g2 = o
    .inject([:]) { m, i ->
        if (i.key.contains('a'))
            m[i.key.toUpperCase()] = i.value
        return m
    }
println g2 // [A:1, AA:5]

Wednesday, December 14, 2011

Joda-Time

@Grab('joda-time:joda-time:2.0')
import org.joda.time.*
//see also http://groovycookbook.org/basic_types/dates_times_joda/

public boolean isAfterPayDay(DateTime datetime) {
  if (datetime.getMonthOfYear() == 2) {   // February is month 2!!
    return datetime.getDayOfMonth() > 26;
  }
  return datetime.getDayOfMonth() > 28;
}

public Days daysToNewYear(LocalDate fromDate) {
  LocalDate newYear = fromDate.plusYears(1).withDayOfYear(1);
  return Days.daysBetween(fromDate, newYear);
}

public boolean isRentalOverdue(DateTime datetimeRented) {
  Period rentalPeriod = new Period().withDays(2).withHours(12);
  return datetimeRented.plus(rentalPeriod).isBeforeNow();
}

public String getBirthMonthText(LocalDate dateOfBirth) {
  return dateOfBirth.monthOfYear().getAsText(Locale.ENGLISH);
}

DateTime dt = new DateTime();
int month = dt.getMonthOfYear();

DateTime year2000 = dt.withYear(2000);
DateTime twoHoursLater = dt.plusHours(2);

String monthName = dt.monthOfYear().getAsText();
String frenchShortName = dt.monthOfYear().getAsShortText(Locale.FRENCH);
boolean isLeapYear = dt.year().isLeap();
DateTime rounded = dt.dayOfMonth().roundFloorCopy();

println (new DateTime().withDayOfMonth(4).withMonthOfYear(7).withYear(1899).toString('EEE, dd/MM/yyyy HH:mm:ss'))

println (new Date().parse("d/M/yyyy H:m:s","21/3/2008 17:30:20"))
println (Calendar.instance.time.format('MMM dd, yyyy'))
// standard groovy flavor, see also http://groovycookbook.org/basic_types/dates_times/

Various index techniques

enum Coin {
    penny(1), nickel(5), dime(10), quarter(25)
    private final int value
    Coin(int value) { this.value = value }
    public int value() { return value }
}
assert 'penny' == Coin.penny.toString()
assert 1 == Coin.penny.value

Map Coin2 = [penny:1, nickel:5, dime:10, quarter:25]
assert 1 == Coin2.penny

List Coin3 = ['penny', 'nickel']
assert 1 == Coin3.indexOf('nickel')

Monday, December 5, 2011

Gretty

@GrabResolver(name='gretty', 
  root='http://groovypp.artifactoryonline.com/groovypp/libs-releases-local')
@Grab('org.mbte.groovypp:gretty:0.4.302') 
import org.mbte.gretty.httpserver.* 

GrettyServer server = []
server.groovy = [ 
    localAddress: new InetSocketAddress("localhost", 8080), 
    defaultHandler: { 
        response.redirect "/" 
    }, 
    "/:name": {
        get {
            response.text = "Hello ${request.parameters['name']}"
        } 
    } 
] 
server.start()

//see: https://github.com/groovypp/gretty/wiki/Getting-Started

see: Gretty, article

CSV file parser I/O

@Grab(group = 'net.sf.opencsv', module = 'opencsv', version = '2.3')
import au.com.bytecode.opencsv.CSVReader
import au.com.bytecode.opencsv.CSVParser
import au.com.bytecode.opencsv.CSVWriter

String TEST_FILE_NAME = 'C:\\test.csv'
String TEST_OUTPUT_FILE_NAME = 'C:\\testOut.csv'

List< String[] > rows = new CSVReader(
    new FileReader(new File(TEST_FILE_NAME)), 
    CSVParser.DEFAULT_SEPARATOR, 
    CSVParser.DEFAULT_ESCAPE_CHARACTER, 
    CSVParser.DEFAULT_QUOTE_CHARACTER, 1
).readAll()

List< String[] > rowsOver100 = rows.findAll { it[1].toInteger() > 100 }

File output = new File(TEST_OUTPUT_FILE_NAME)
if (output.exists()) {
    output.delete()
}

output.createNewFile()
output.withWriter { writer ->
    new CSVWriter(writer).writeAll(rowsOver100)
}

// for streaming, see also: http://www.groovy-tutorial.org/basic-csv/