Tuesday, April 24, 2012

CodeNarc bundle

Simple starter zip file for running CodeNarc (groovy static analysis) 0.17 inside a local groovy source code folder. (Windows .bat file) Generates html report page.

Wednesday, March 28, 2012

JRegex

@Grapes(
    @Grab(group='net.sourceforge.jregex', module='jregex', version='1.2_01')
)
import jregex.*
//http://jregex.sourceforge.net/gstarted-advanced.html#imatching

String userNameChars="[\\w.\\-]+" //letters, dots, hyphens
String alphaNums="\\w+"
String dot="\\."
Pattern email=new Pattern(userNameChars + "@" + "(?:"+ alphaNums + dot + ")*" + alphaNums)

List ls = ['s@1.com','2@3.com','howdy@d.']

ls.each {
    println email.matcher(it).matches()
}

Pattern re=new Pattern("\\d\\d:\\d\\d") //two digits + colon + two digits
println("Pattern: "+re)

Matcher m=re.matcher()
test(m,"") // true
test(m,"1") // true
test(m,"12") // true
test(m,"1:2") // true
test(m,"12:") // true
test(m,"12:1") // true
test(m,"12:12") // true
test(m,"12:123") // true
test(m,"1:") // false
test(m,"123:1") // false

void test(Matcher m1, String s) {
   m1.setTarget(s)
   println("\""+s+"\" : "+m1.matchesPrefix()) //note: .matchesPrefix()
}

////*Pure Java/Groovy*////
//credit: http://glaforge.appspot.com/article/incomplete-string-regex-matching
List inp = [
    /12:/,/12:1/,/1/,/12:12/ //trues
    ,/1:/,/123:1/ //falses
]

inp.each {
    def matcher = it =~ /\d{2}:\d{2}/
    println matcher.matches() || matcher.hitEnd()
}

println 'done'

Sunday, March 4, 2012

Thread Pools

import java.util.concurrent.Callable
import java.util.concurrent.Executors

long ts // timer
def pool = Executors.newFixedThreadPool(THREADS)
def defer = { c -> pool.submit(c as Callable) }

//PLAIN//////////////////////////////////////////

ts = (new Date().time)
doit = { n ->
  def left = { def slp = Math.random()*n*1000+500l as Long; Thread.sleep(slp);  println Thread.currentThread().name + '!!left!!'+slp }()
  def right = { def slp = Math.random()*n*1000+500l as Long; Thread.sleep(slp);  println Thread.currentThread().name + '!!right!!'+slp }()
}
(1..3).each{ n -> println "n=$n => ${doit(n)}" }
println ((new Date().time)-ts)

//POOLED//////////////////////////////////////////////

ts = (new Date().time)
THREADS = 3
fib = { n ->
  def left = defer{ def slp = Math.random()*n*1000+500l as Long; Thread.sleep(slp);  println Thread.currentThread().name + '!!left!!'+slp }
  def right = defer{ def slp = Math.random()*n*1000+500l as Long; Thread.sleep(slp);  println Thread.currentThread().name + '!!right!!'+slp }
}
(1..3).each{ n -> println "n=$n => ${fib(n)}" }
pool.shutdown()
while(!pool.isTerminated()) { Thread.sleep(100) }
println ((new Date().time)-ts)

//FIB//////////////////////////////////////////////////

println "Calculating Fibonacci sequence in parallel..."
CUTOFF = 12    // not worth parallelizing for small n
THREADS = 10
serialFib = { n -> (n < 2) ? n : serialFib(n-1) + serialFib(n-2) }
fib = { n ->
  if (n < CUTOFF) return serialFib(n)
  def left = defer{ println Thread.currentThread().name; fib(n-1) }
  def right = defer{ println Thread.currentThread().name; fib(n-2) }
  left.get() + right.get()
}

(11..16).each{ n -> println "n=$n => ${fib(n)}" }
pool.shutdown()

Thursday, March 1, 2012

Jetty server

//src: http://www.redtoad.ca/ataylor/2012/02/simple-servlets-in-groovy/

@Grab(group='org.mortbay.jetty', module='jetty-embedded', version='6.1.26')
import org.mortbay.jetty.Server
import org.mortbay.jetty.servlet.*
import groovy.servlet.*;
import javax.servlet.http.*;
import javax.servlet.ServletConfig;
 
class SimpleGroovyServlet extends HttpServlet {
    def requestHandler
    def context
    void init(ServletConfig config) {
        super.init(config)
        context = config.servletContext
    }
    void service(HttpServletRequest request, HttpServletResponse response) {
        requestHandler.binding = new ServletBinding(request, response, context)
        use (ServletCategory) {
            requestHandler.call()
        }
    }
    static void run(int port, Closure requestHandler) {
        def servlet = new SimpleGroovyServlet(requestHandler: requestHandler)
        def jetty = new Server(port)
        def context = new Context(jetty, '/', Context.SESSIONS)
        context.addServlet(new ServletHolder(servlet), '/*')
        jetty.start()
    }
}

SimpleGroovyServlet.run(8080) { ->
    response.contentType = 'text/plain'
    println "hello world!"
    println "my path is ${request.pathInfo}"
    println "my params are $params"
}

//also try: groovy -l 8080 -e "println 'hello world'"
//alternate: https://github.com/groovypp/gretty/wiki/Getting-Started

Wednesday, February 29, 2012

collate

//src: http://blog.bloidonia.com/post/18073244930/whats-new-in-groovy-1-8-6-the-collate-method

//v1.8.6+//
def list = 1..10
def listInFours  = list.collate( 4 )
assert listInFours == [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10 ] ]

def listInFours2 = list.collate( 4, false )

// Notice the [ 9, 10 ] remainder has been dropped
assert listInFours2 == [ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ] ]

def gridList = list.collate( 3 ).collate( 2 )

assert gridList == [ [ [1, 2, 3], [4, 5, 6] ],
                     [ [7, 8, 9], [10]      ] ]

def mph        = [ 10, 26, 30, 32, 27, 14, 19, 22, 40, 14 ]
def window     = mph.collate( 3, 1, false )

assert window == [ [10, 26, 30],
                   [26, 30, 32],
                   [30, 32, 27],
                   [32, 27, 14],
                   [27, 14, 19],
                   [14, 19, 22],
                   [19, 22, 40],
                   [22, 40, 14] ]
                   
def windowAvg  = window*.sum()*.intdiv( 3 )
assert windowAvg == [22, 29, 29, 24, 20, 18, 27, 25]

def items   = [ 'ham', 30, 'cheese', 20, 'steak', 95 ]
def itemMap = items.collate( 2 ).collectEntries()

assert itemMap == [ ham:30, cheese:20, steak:95 ]

def range   = 1..20
def (odds,evens) = range.collate( 2 ).transpose()

assert odds  == (1..19).step( 2 ) // odd numbers 1 - 19
assert evens == (2..20).step( 2 ) // even numbers 2 - 20

Monday, February 27, 2012

Regex look-ahead/behind syntax

//http://groovy.codehaus.org/Regular+Expressions
def r = /e+/
def str = 'me cheese please'
def m = str =~ r

assert m.size() == 5
println m*.toString() //[e, ee, e, e, e]

/////////////////////////////

def r = /(This boy) is/
def str = 'This boy is 10. This boy wants chocolate. This boy is tall.'
def m = str =~ r

assert m.size() == 2
assert m.collect { it[1] } == ['This boy','This boy']

/////////////////////////////

assert "abc".replaceAll(/(a)(b)(c)/, "\$1\$3") == 'ac' //back references

/////////////////////////////

//http://www.regular-expressions.info/captureall.html
r = /((?:abc|123)+).*?/
str = '123abc 123abc123'
m = str =~ r

assert m.size() == 2
println m*.toString()
print m[0][1..-1]
println m[1][1..-1]

/////////////////////////////

//password of 8 characters long and two non-letters
def r1 = /.*[^a-zA-Z].*[^a-zA-Z].*(?<=.{7})/  //look-behind ?<=

assert 'abc' !=~ r1
assert 'abcde12' !=~ r1
assert 'abcdef12' ==~ r1
assert 'abc1defgggg2' ==~ r1
assert 'abc1defgggg' !=~ r1

//word is foo$wrd*, store $wrd*
def wrd = 'bar'
def r2 = /foo((?=$wrd)[\w]+)/  //look-ahead ?=

def foo = "foo${wrd}hellow"
assert foo ==~ r2
def m = foo =~ r2
assert m[0][1] == "${wrd}hellow" // note: $wrd not consumed by check, is stored in result

foo = 'foohellow' // no $wrd
assert foo !=~ r2

//ADVANCED/////////////////////////////
def churnText(String text) {
    def points = [[k: ~/(N|n)orth(E|e)ast(ern)?/                                                                , v:'NE'],
              [k: ~/(?>(N|n)orth(W|w)est(ern|:)?)(?! Territories)/                                              , v:'NW'],
              [k: ~/(S|s)outheast(ern)?/                                                                        , v:'SE'],
              [k: ~/(?>(S|s)outh(\s)?(W|w)est(ern)?)(?! Hill| Bend)/                                            , v:'SW'],
              [k: ~/(?>(N|n)orth(ern)?|Upstate)(?! Carolina| Dakota| Platte| Neck| Mariana Islands| Bay|ridge)/ , v:'N' ],
              [k: ~/(E|e)ast(ern)?/                                                                             , v:'E' ],
              [k: ~/(?>(S|s)outh(ern|side)?)(?! Carolina| Dakota)/                                              , v:'S' ],
              [k: ~/(?!(?<=George ))(?>(W|w)est(ern| of the)?)(?! Virginia| Palm Beach)/                        , v:'W' ],
              [k: ~/(?>(C|c)entral|Center|Middle|the middle section of the)(?!town| Peninsula| Tennessee|ia)/   , v:'C' ]]
    
    points.each {p ->
      def matcher = (text =~ p.k)
      text = matcher.replaceAll(p.v)
      println "p.v: ${p.v} text: $text"
    }
}

churnText('Northwest Virginia')
println '='*40
churnText('NorthWestern Territories')
println '='*40
churnText('East George West')
return

Thursday, February 23, 2012

Swing Notification Popup

//see: http://groovy.codehaus.org/GUI+Programming+with+Groovy

import groovy.swing.*
import java.awt.*
import javax.swing.*
import javax.swing.border.*
import groovy.util.slurpersupport.NodeChild as Node
 
public class Notify { 
 def sb = new SwingBuilder()

 public static void main(String[] args) {
  // creates and displays itself
  new Notify(args)
 }

 public Notify(String[] args){
  // arg is path to xml message file, 
  // will be deleted after popup displayed
  if (args.size() != 1) return 
  
  String appTitle = ".:. Notification .:."
  String filePath = args[0]
  Color backgroundColor = Color.yellow
  Border emptyBorder = BorderFactory.createMatteBorder(5, 5, 5, 5, backgroundColor)
    
  Toolkit tk = Toolkit.getDefaultToolkit()
  Dimension screenSize = tk.getScreenSize()
  final int WIDTH = screenSize.width
  final int HEIGHT = screenSize.height
  
  int w = WIDTH / 2
  int h = HEIGHT / 2
  
  int x = (WIDTH / 2) - (w / 2)
  int y = (HEIGHT / 2) - (h / 2) - 50
  
  Font fontSubject = new Font("Serif", Font.PLAIN, 24)
  Font fontMessage = new Font("Serif", Font.PLAIN, 16)
  
  //generate frame//
  sb.dialog(id:"popupBox", title:appTitle, visible:true, alwaysOnTop:true, defaultCloseOperation:JFrame.DISPOSE_ON_CLOSE, resizable:false, location:[x, y], size:[w, h]) 
  {
   borderLayout()
   label(id:"subject", constraints:BorderLayout.NORTH, horizontalAlignment:JTextField.CENTER, font:fontSubject, "SUBJ")
   
   panel(constraints:BorderLayout.CENTER){
    gridLayout(cols:1, rows:1, hgap:1, vgap:1)
    scrollPane(border:emptyBorder) {
     textArea(id:"message", editable:false, lineWrap:true, wrapStyleWord:true, font:fontMessage, "MESG")
    }
   }
  }
  def frame = sb.popupBox
  
  //decorate frame color//
  def cp = frame.contentPane
  cp.background = backgroundColor
  sb.message.background = cp.background
  
  //populate frame data//
  File xmlFile
  Node root
  try {
   xmlFile = new File(filePath)
   root = new XmlSlurper().parse(xmlFile)
   //println root.getClass().name
  } 
  catch (Exception) {
   xmlFile = new File("")
   root = new XmlSlurper().parseText('''
    Error
    Sorry, file path or XML syntax error.
    ''')
  }
  
  sb.subject.text = root.subject.text() // if not found, subject UI label is hidden
  sb.message.text = root.message.text() ? 
   root.message.text().replaceAll('\\\\n', System.getProperty( "line.separator" )) :
   'Sorry, empty notification.'
  
  //display form//
  frame.show()
  
  try {
   xmlFile.delete()
  } catch (Exception ignore) {}
 }
}