diff --git a/jdb-build-1.6.xml b/jdb-build-1.6.xml index da3fa52..760c821 100644 --- a/jdb-build-1.6.xml +++ b/jdb-build-1.6.xml @@ -59,7 +59,9 @@ - + + + @@ -96,7 +98,7 @@ - + @@ -109,7 +111,7 @@ - + diff --git a/libpit/project.properties b/libpit/project.properties index a85e2f0..8e11113 100755 --- a/libpit/project.properties +++ b/libpit/project.properties @@ -1,11 +1,11 @@ -#Tue, 22 Nov 2011 14:32:12 -0600 +#Wed, 07 Dec 2011 17:53:14 -0600 #Sat Apr 24 17:08:00 CDT 2010 build.dir=build src.dir=src lib.shared.dir=../shared-libs test.dir=test -build.number=25 -version=3.0.0 +build.number=8 +version=3.1.0 name=libpit lib.dir=lib lib.local=true diff --git a/libpit/src/main/com/jdbernard/pit/ExtendedPropertyHelp.groovy b/libpit/src/main/com/jdbernard/pit/ExtendedPropertyHelp.groovy new file mode 100644 index 0000000..5aeb92a --- /dev/null +++ b/libpit/src/main/com/jdbernard/pit/ExtendedPropertyHelp.groovy @@ -0,0 +1,81 @@ +package com.jdbernard.pit + +import org.joda.time.DateMidnight +import org.joda.time.DateTime + +import java.text.SimpleDateFormat + +public enum ExtendedPropertyHelp { + + // Property types should be ordered here in order of decreasing specificity. + // That is, subclasses should come before the more general class so that + // objects are converted using the most specific class that + // ExtendedPropertyHelp knows how to work with. + DATE_MIDNIGHT(/^\d{4}-\d{2}-\d{2}$/, DateMidnight, + { v -> DateMidnight.parse(v) }, + { d -> d.toString("YYYY-MM-dd") }), + DATETIME(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/, DateTime, + { v -> DateTime.parse(v) }, + { d -> d.toString("YYYY-MM-dd'T'HH:mm:ss") }), + // We never want to parse a value into a java.util.Date or + // java.util.Calendar object (we are using Joda Time instead of the + // standard Java Date and Calendar objects) but we do want to be able to + // handle if someone gives us a Date or Calendar object. + DATE(NEVER_MATCH, Date, + { v -> v }, // never called + { d -> dateFormat.format(d) }), + CALENDAR(NEVER_MATCH, Calendar, + { v -> v }, // never called + { c -> + def df = dateFormat.clone() + df.calendar = c + df.format(c.time) }), + + INTEGER(NEVER_MATCH, Integer, + { v -> v as Integer }, // never called + { i -> i as String }), + LONG(/^\d+$/, Long, + { v -> v as Long }, + { l -> l as String }), + FLOAT(NEVER_MATCH, Float, + { v -> v as Float}, // never called + { f -> f as String}), + DOUBLE(/^\d+\.\d+$/, Double, + { v -> v as Double }, + { d -> d as String }); + + String pattern; + Class klass; + def parseFun, formatFun; + + private static SimpleDateFormat dateFormat = + new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss"); + + // This pattern for can never match (is uses negative lookahead to + // contradict itself). + private static String NEVER_MATCH = /(?!x)x/; + + + public ExtendedPropertyHelp(String pattern, Class klass, def parseFun, + def formatFun) { + this.pattern = pattern + this.klass = klass + this.parseFun = parseFun + this.formatFun = formatFun } + + public boolean matches(String prop) { return prop ==~ pattern } + + public boolean matches(Class klass) { return this.klass == klass } + + public static Object parse(String value) { + def propertyType = ExtendedPropertyHelp.values().find { + it.matches(value) } + + return propertyType ? propertyType.parseFun(value) : value } + + public static String format(def object) { + def propertyType = ExtendedPropertyHelp.values().find { + it.klass.isInstance(object) } + + return propertyType ? propertyType.formatFun(object) : object.toString() } +} diff --git a/libpit/src/main/com/jdbernard/pit/Filter.groovy b/libpit/src/main/com/jdbernard/pit/Filter.groovy index 239bb6f..2bbcfaa 100755 --- a/libpit/src/main/com/jdbernard/pit/Filter.groovy +++ b/libpit/src/main/com/jdbernard/pit/Filter.groovy @@ -8,8 +8,8 @@ class Filter { List ids = null int priority = 9 boolean acceptProjects = true - Closure issueSorter = defaultIssueSorter - Closure projectSorter = defaultProjectSorter + def issueSorter = defaultIssueSorter + def projectSorter = defaultProjectSorter public static Closure defaultIssueSorter = { it.id.toInteger() } public static Closure defaultProjectSorter = { it.name } diff --git a/libpit/src/main/com/jdbernard/pit/Project.groovy b/libpit/src/main/com/jdbernard/pit/Project.groovy index 9796e9c..9135f23 100755 --- a/libpit/src/main/com/jdbernard/pit/Project.groovy +++ b/libpit/src/main/com/jdbernard/pit/Project.groovy @@ -10,30 +10,32 @@ public abstract class Project { public void eachIssue(Filter filter = null, Closure c) { def sorter = filter?.issueSorter ?: Filter.defaultIssueSorter - for (i in issues.values().sort(sorter)) + for (i in sort(issues.values(), sorter)) if (!filter || filter.accept(i)) - c.call(i) - } + c.call(i) } public void eachProject(Filter filter = null, Closure c) { def sorter = filter?.projectSorter ?: Filter.defaultProjectSorter - for (p in projects.values().sort(sorter)) + for (p in sort(projects.values(), sorter)) if (!filter || filter.accept(p)) - c.call(p) - } + c.call(p) } // walk every issue and project in this project recursively and execute the // given closure on each issue that meets the filter criteria public void walkProject(Filter filter, Closure c) { this.eachIssue(filter, c) - this.eachProject(filter) { p -> p.walkProject(filter, c) } - } + this.eachProject(filter) { p -> p.walkProject(filter, c) } } // This get all issues, including subissues public List getAllIssues(Filter filter = null) { - List result = this.issues.findAll { filter.accept(it) } - this.eachProject(filter) { p -> result += p.getAllIssues(filter) } - } + def sorter = filter?.issueSorter ?: Filter.defaultIssueSorter + + List allIssues = this.issues.values().findAll { + filter ? filter.accept(it) : true } + + this.eachProject(filter) { p -> allIssues += p.getAllIssues(filter) } + + return sort(allIssues, sorter) } public void setName(String name) { this.name = name } @@ -49,4 +51,10 @@ public abstract class Project { public abstract boolean deleteIssue(Issue issue) public abstract boolean deleteProject(Project project) + + protected List sort(def collection, def sorter) { + if (sorter instanceof Closure) { + return collection.sort(sorter) } + else if (sorter instanceof List) { + return sorter.reverse().inject(collection) { c, s -> c.sort(s) }}} } diff --git a/libpit/src/main/com/jdbernard/pit/Status.groovy b/libpit/src/main/com/jdbernard/pit/Status.groovy index d1a227b..1779370 100755 --- a/libpit/src/main/com/jdbernard/pit/Status.groovy +++ b/libpit/src/main/com/jdbernard/pit/Status.groovy @@ -12,25 +12,25 @@ public enum Status { protected Status(String s) { symbol = s } public static Status toStatus(String str) { - Status retVal = null - for(status in Status.values()) { - if (status.symbol.equalsIgnoreCase(str) || - status.name().startsWith(str.toUpperCase())) { + // Try to match based on symbol + def match = Status.values().find {it.symbol.equalsIgnoreCase(str)} + if (match) { return match } - if (retVal != null) - throw new IllegalArgumentException("Request string is" + - " ambigous, '${str}' could represent ${retVal} or " + - "${status}, possibly others.") + // No match on the symbol, look for the status name (or abbreviations) + match = Status.values().findAll { + it.name().startsWith(str.toUpperCase()) } - retVal = status - } - } + // No matching status, oops. + if (match.size() == 0) { + throw new IllegalArgumentException("No status matches '${str}'") } - if (retVal == null) - throw new IllegalArgumentException("No status matches '${str}'") + // More than one matching status, oops. + else if (match.size() > 1) { + throw new IllegalArgumentException("Request string is" + + " ambigous, '${str}' could represent any of ${match}.")} - return retVal - } + // Only one matching status, yay! + else { return match[0] }} public String toString() { def words = name().split("_") diff --git a/libpit/src/main/com/jdbernard/pit/file/ExtendedPropertyHelp.groovy b/libpit/src/main/com/jdbernard/pit/file/ExtendedPropertyHelp.groovy deleted file mode 100644 index cd5bf13..0000000 --- a/libpit/src/main/com/jdbernard/pit/file/ExtendedPropertyHelp.groovy +++ /dev/null @@ -1,45 +0,0 @@ -package com.jdbernard.pit.file - -import org.joda.time.DateMidnight -import org.joda.time.DateTime - -public enum ExtendedPropertyHelp { - - DATE(/^\d{4}-\d{2}-\d{2}$/, DateMidnight, - { v -> DateMidnight.parse(v) }, - { d -> d.toString("YYYY-MM-dd") }), - DATETIME(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/, DateTime, - { v -> DateTime.parse(v) }, - { d -> d.toString("YYYY-MM-dd'T'HH:mm:ss") }), - INTEGER(/^\d+$/, Integer, - { v -> v as Integer }, - { i -> i as String }); - - String pattern; - Class klass; - def parseFun, formatFun; - - public ExtendedPropertyHelp(String pattern, Class klass, def parseFun, - def formatFun) { - this.pattern = pattern - this.klass = klass - this.parseFun = parseFun - this.formatFun = formatFun } - - public boolean matches(String prop) { return prop ==~ pattern } - - public static Object parse(String value) { - def result = null - ExtendedPropertyHelp.values().each { propType -> - if (propType.matches(value)) { result = propType.parseFun(value) }} - - return result ?: value } - - public static String format(def object) { - def result = null - ExtendedPropertyHelp.values().each { propType -> - if (!result && propType.klass.isInstance(object)) { - result = propType.formatFun(object) }} - - return result ?: object.toString() } -}