Continued implementation of extended attributes.

* Changed the `Issue` constructor to use an attribute map instead of an
  increasingly long parameter list. Of course we lose some control over required
  parameters.
* Added the Joda Time and SLF4J logging libraries.
* Implemented the `FileIssue` constructor for the new `Issue` refactor.
This commit is contained in:
Jonathan Bernard 2011-11-05 08:44:47 -05:00
parent 5ff4665a07
commit f86316c68f
9 changed files with 85 additions and 24 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,10 +1,10 @@
#Wed, 26 Oct 2011 14:20:47 -0500 #Thu, 03 Nov 2011 13:47:05 -0500
#Sat Apr 24 17:08:00 CDT 2010 #Sat Apr 24 17:08:00 CDT 2010
build.dir=build build.dir=build
src.dir=src src.dir=src
lib.shared.dir=../shared-libs lib.shared.dir=../shared-libs
test.dir=test test.dir=test
build.number=0 build.number=11
expected.application.version=3.0.0 expected.application.version=3.0.0
lib.dir=lib lib.dir=lib
release.dir=release release.dir=release

View File

@ -13,13 +13,20 @@ public abstract class Issue {
protected Map extendedPropeties = [:] protected Map extendedPropeties = [:]
Issue(String id, Category c = Category.TASK, Status s = Status.NEW, Issue(Map props) {
int p = 9) { this.id = props.id
this.id = id this.category = props.category ?: Category.TASK
this.category = c this.status = props.status ?: Status.NEW
this.status = s this.priority = props.priority ?: 9
this.priority = p this.title = props.title ?: ''
} this.text = props.text ?: ''
def nativeProps =
["id", "category", "status", "priority", "title", "text"]
props.each { key, val ->
if (nativeProps.contains(key)) { return }
this.extendedProperties[key] = val }}
public String getId() { return id; } public String getId() { return id; }
@ -49,7 +56,7 @@ public abstract class Issue {
public String getTitle() { return title } public String getTitle() { return title }
public String setTitle(String t) throws IOException { title = t } public void setTitle(String t) throws IOException { title = t }
public String getText() { return text } public String getText() { return text }

View File

@ -1,31 +1,75 @@
package com.jdbernard.pit.file package com.jdbernard.pit.file
import com.jdbernard.pit.* import com.jdbernard.pit.*
import java.lang.IllegalArgumentException as IAE import java.lang.IllegalArgumentException as IAE
import org.joda.time.DateMidnight
import org.joda.time.DateTime
import org.slf4j.Logger
import org.slf4j.LoggerFactory
public class FileIssue extends Issue { public class FileIssue extends Issue {
protected File source protected File source
private Logger log = LoggerFactory.getLogger(getClass())
public static final String fileExp = /(\d+)([bft])([ajnsv])(\d).*/ public static final String fileExp = /(\d+)([bft])([ajnsv])(\d).*/
public FileIssue(File file) { public FileIssue(File file) {
super('REPLACE_ME') super(id: -1, title: 'REPLACE_ME')
if (log.isDebugEnabled()) {
log.debug("Loading a FileIssue from '{}'", file.canonicalPath) }
def matcher = file.name =~ fileExp def matcher = file.name =~ fileExp
if (!matcher) if (!matcher)
throw new IllegalArgumentException("${file} " + throw new IllegalArgumentException("${file} " +
"is not a valid Issue file.") "is not a valid Issue file.")
// Read issue attributes from the filename.
super.@id = matcher[0][1] super.@id = matcher[0][1]
super.@category = Category.toCategory(matcher[0][2]) super.@category = Category.toCategory(matcher[0][2])
super.@status = Status.toStatus(matcher[0][3]) super.@status = Status.toStatus(matcher[0][3])
super.@priority = matcher[0][4].toInteger() super.@priority = matcher[0][4].toInteger()
log.debug("id: {}\tcategory: {}\tstatus: {}\tpriority: {}",
[super.@id, super.@category, super.@status, super.@priority])
this.source = file this.source = file
super.@text = file.text String text = ""
super.@title = super.@text.readLines()[0] boolean parsingText = true
// Now parse the actual file contents.
file.text.eachLine { line, lineNumber ->
log.trace("lineNumber: {}, line: {}", lineNumber, line)
if (lineNumber == 0) {
super.@title = line
log.debug("Found title: {}", super.@title) }
else if (lineNumber > 2) {
// We see the horizontal rule
if (line ==~ /\s*^\-{4}\-*\s*$/) { parsingText = false }
if (parsingText) { text += "$line\n" }
else {
def match = (line =~ /^([^:]+):(.+)$/)
if (match) {
def key = match[0][1].trim()
def value = parseValue(match[0][2].trim())
super.@extendedProperties[key] = value
}
}
}
}
super.@text = text
} }
public void setCategory(Category c) throws IOException { public void setCategory(Category c) throws IOException {
@ -109,22 +153,22 @@ public class FileIssue extends Issue {
public static String formatIssue(Issue issue) { public static String formatIssue(Issue issue) {
def result = new StringBuilder() def result = new StringBuilder()
result.append(title) result.append(issue.@title)
result.append("\n") result.append("\n")
result.append("=".multiply(title.length())) result.append("=".multiply(issue.@title.length()))
result.append("\n") result.append("\n")
result.append(text) result.append(issue.@text)
result.append("\n----\n") result.append("\n----\n")
// If there are any extended properties, let's write those. // If there are any extended properties, let's write those.
if (super.@extendedProperties.size() > 0) { if (issue.@extendedProperties.size() > 0) {
def extOutput = [:] def extOutput = [:]
def maxKeyLen = 0 def maxKeyLen = 0
def maxValLen = 0 def maxValLen = 0
// Find the longest key and value, convert all to strings. // Find the longest key and value, convert all to strings.
super.@extendedProperties.each { key, val -> issue.@extendedProperties.each { key, val ->
def ks = key.toString(), vs = val.toString() def ks = key.toString(), vs = formatProperty(val)
extOutput[ks] = vs extOutput[ks] = vs
if (ks.length() > maxKeyLen) { maxKeyLen = ks.length() } if (ks.length() > maxKeyLen) { maxKeyLen = ks.length() }
if (vs.length() > maxKeyLen) { maxValLen = vs.length() } } if (vs.length() > maxKeyLen) { maxValLen = vs.length() } }
@ -146,7 +190,7 @@ public class FileIssue extends Issue {
result.append("\n") }} result.append("\n") }}
protected void writeFile() { protected void writeFile() {
try { source.write(formatIssue(this) } try { source.write(formatIssue(this)) }
catch (IOException ioe) { catch (IOException ioe) {
throw new IOException("I could not save the new text for this " throw new IOException("I could not save the new text for this "
+ "issue. I can not write to the file for this issue. I do not" + "issue. I can not write to the file for this issue. I do not"
@ -154,4 +198,16 @@ public class FileIssue extends Issue {
} }
} }
public def parseValue(String value) {
switch (value) {
case ~/\d{4}-\d{2}-\d{2}/: return DateMidnight.parse(value)
default: return value
}
}
public String formatProperty(DateTime prop) {
return prop.format("YYYY-MM-dd'T'HH:mm:ss") }
public String formatProperty(DateMidnight prop) {
return prop.format("YYYY-MM-dd") }
} }

View File

@ -2,7 +2,7 @@ package com.jdbernard.pit
public class MockIssue extends Issue { public class MockIssue extends Issue {
public MockIssue(String id, Category c, Status s, int p) { public MockIssue(String id, Category c, Status s, int p) {
super (id, c, s, p) super ([id: id, category: c, status: s, priority: p])
} }
public boolean delete() { return true } public boolean delete() { return true }
} }

View File

@ -171,9 +171,7 @@ class FileIssueTest {
assertEquals issue.status , Status.NEW assertEquals issue.status , Status.NEW
assertEquals issue.priority , 1 assertEquals issue.priority , 1
assertEquals issue.title , "Add the killer feature to the killer app." assertEquals issue.title , "Add the killer feature to the killer app."
assertEquals issue.text , "Add the killer feature to the killer app.\n" + assertEquals issue.text , "Make our killer app shine!."
"=========================================\n\n" +
"Make our killer app shine!."
assertEquals issue.source , issueFile assertEquals issue.source , issueFile
} }