Moved to JDB common build structure.
This commit is contained in:
17
libpit/src/main/com/jdbernard/pit/Category.groovy
Executable file
17
libpit/src/main/com/jdbernard/pit/Category.groovy
Executable file
@ -0,0 +1,17 @@
|
||||
package com.jdbernard.pit
|
||||
|
||||
public enum Category {
|
||||
BUG,
|
||||
FEATURE,
|
||||
TASK
|
||||
|
||||
public static Category toCategory(String s) {
|
||||
for(c in Category.values())
|
||||
if (c.name().startsWith(s.toUpperCase())) return c
|
||||
throw new IllegalArgumentException("No category matches ${s}.")
|
||||
}
|
||||
|
||||
public String getSymbol() { toString()[0].toLowerCase() }
|
||||
|
||||
public String toString() { return "${name()[0]}${name()[1..-1].toLowerCase()}" }
|
||||
}
|
33
libpit/src/main/com/jdbernard/pit/Filter.groovy
Executable file
33
libpit/src/main/com/jdbernard/pit/Filter.groovy
Executable file
@ -0,0 +1,33 @@
|
||||
package com.jdbernard.pit
|
||||
|
||||
class Filter {
|
||||
|
||||
List<Category> categories = null
|
||||
List<Status> status = null
|
||||
List<String> projects = null
|
||||
List<String> ids = null
|
||||
int priority = 9
|
||||
boolean acceptProjects = true
|
||||
Closure issueSorter = defaultIssueSorter
|
||||
Closure projectSorter = defaultProjectSorter
|
||||
|
||||
public static Closure defaultIssueSorter = { it.id.toInteger() }
|
||||
public static Closure defaultProjectSorter = { it.name }
|
||||
|
||||
public boolean accept(Issue i) {
|
||||
return (i.priority <= priority &&
|
||||
(!categories || categories.contains(i.category)) &&
|
||||
(!status || status.contains(i.status)) &&
|
||||
(!ids || ids.contains(i.id)))
|
||||
}
|
||||
|
||||
public boolean accept(Project p) {
|
||||
return (acceptProjects &&
|
||||
(!projects || projects.contains(p.name)))
|
||||
}
|
||||
|
||||
public boolean accept(String name) {
|
||||
return (acceptProjects &&
|
||||
(!projects || projects.contains(name)))
|
||||
}
|
||||
}
|
37
libpit/src/main/com/jdbernard/pit/FlatProjectView.groovy
Executable file
37
libpit/src/main/com/jdbernard/pit/FlatProjectView.groovy
Executable file
@ -0,0 +1,37 @@
|
||||
package com.jdbernard.pit
|
||||
|
||||
public class FlatProjectView extends Project {
|
||||
|
||||
public FlatProjectView(String name) { super(name) }
|
||||
|
||||
public Issue createNewIssue(Map options) {
|
||||
throw new UnsupportedOperationException("The FlatProjectView is " +
|
||||
"read-only.")
|
||||
}
|
||||
|
||||
public Project createNewProject(String name) {
|
||||
throw new UnsupportedOperationException("The FlatProjectView is " +
|
||||
"read-only.")
|
||||
}
|
||||
|
||||
public boolean deleteIssue(Issue issue) { return false }
|
||||
public boolean deleteProject(Project project) { return false }
|
||||
|
||||
public boolean delete() { return true }
|
||||
|
||||
public void eachIssue(Filter filter = null, Closure closure) {
|
||||
def sorter = filter?.issueSorter ?: Filter.defaultIssueSorter
|
||||
def gatherIssues
|
||||
def gatheredIssues = []
|
||||
|
||||
gatherIssues = { project, f ->
|
||||
project.eachIssue(f) { gatheredIssues << it }
|
||||
project.eachProject(f) { gatherIssues(it, f) }
|
||||
}
|
||||
for (p in projects.values())
|
||||
if (!filter || filter.accept(p))
|
||||
gatherIssues(p, filter)
|
||||
|
||||
gatheredIssues.sort(sorter).each(closure)
|
||||
}
|
||||
}
|
75
libpit/src/main/com/jdbernard/pit/Issue.groovy
Executable file
75
libpit/src/main/com/jdbernard/pit/Issue.groovy
Executable file
@ -0,0 +1,75 @@
|
||||
package com.jdbernard.pit
|
||||
|
||||
import java.lang.IllegalArgumentException as IAE
|
||||
|
||||
public abstract class Issue {
|
||||
|
||||
protected String id
|
||||
protected Category category
|
||||
protected Status status
|
||||
protected int priority
|
||||
protected String text
|
||||
protected String title
|
||||
|
||||
protected Map extendedPropeties = [:]
|
||||
|
||||
Issue(Map props) {
|
||||
this.id = props.id
|
||||
this.category = props.category ?: Category.TASK
|
||||
this.status = props.status ?: Status.NEW
|
||||
this.priority = props.priority ?: 9
|
||||
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 Category getCategory() { return category }
|
||||
|
||||
public void setCategory(Category c) throws IOException {
|
||||
if (c == null)
|
||||
throw new IAE("Category cannot be null.")
|
||||
|
||||
this.category = c
|
||||
}
|
||||
|
||||
public Status getStatus() { return status }
|
||||
|
||||
public void setStatus(Status s) throws IOException {
|
||||
if (s == null)
|
||||
throw new IAE("Status cannot be null.")
|
||||
|
||||
this.status = s
|
||||
}
|
||||
|
||||
public int getPriority() { return priority }
|
||||
|
||||
public void setPriority(int p) throws IOException {
|
||||
priority = Math.min(9, Math.max(0, p))
|
||||
}
|
||||
|
||||
public String getTitle() { return title }
|
||||
|
||||
public void setTitle(String t) throws IOException { title = t }
|
||||
|
||||
public String getText() { return text }
|
||||
|
||||
public void setText(String t) throws IOException { text = t }
|
||||
|
||||
public def propertyMissing(String name) { extendedProperties[name] }
|
||||
|
||||
public def propertyMissing(String name, def value) {
|
||||
extendedProperties[name] = value }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "${id}(${priority}-${status}): ${category} ${title}"
|
||||
}
|
||||
|
||||
}
|
52
libpit/src/main/com/jdbernard/pit/Project.groovy
Executable file
52
libpit/src/main/com/jdbernard/pit/Project.groovy
Executable file
@ -0,0 +1,52 @@
|
||||
package com.jdbernard.pit
|
||||
|
||||
public abstract class Project {
|
||||
|
||||
protected String name
|
||||
Map<String, Issue> issues = [:]
|
||||
Map<String, Project> projects = [:]
|
||||
|
||||
Project(String name) { this.name = name }
|
||||
|
||||
public void eachIssue(Filter filter = null, Closure c) {
|
||||
def sorter = filter?.issueSorter ?: Filter.defaultIssueSorter
|
||||
for (i in issues.values().sort(sorter))
|
||||
if (!filter || filter.accept(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))
|
||||
if (!filter || filter.accept(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 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) }
|
||||
}
|
||||
|
||||
public void setName(String name) { this.name = name }
|
||||
|
||||
public String getName() { return name }
|
||||
|
||||
@Override
|
||||
String toString() { return name }
|
||||
|
||||
public abstract Issue createNewIssue(Map options)
|
||||
|
||||
public abstract Project createNewProject(String name)
|
||||
|
||||
public abstract boolean deleteIssue(Issue issue)
|
||||
|
||||
public abstract boolean deleteProject(Project project)
|
||||
}
|
8
libpit/src/main/com/jdbernard/pit/Repository.groovy
Executable file
8
libpit/src/main/com/jdbernard/pit/Repository.groovy
Executable file
@ -0,0 +1,8 @@
|
||||
package com.jdbernard.pit
|
||||
|
||||
public abstract class Repository {
|
||||
|
||||
public abstract void persist()
|
||||
public abstract Project[] getRootProjects()
|
||||
public abstract Project createNewProject(String name)
|
||||
}
|
41
libpit/src/main/com/jdbernard/pit/Status.groovy
Executable file
41
libpit/src/main/com/jdbernard/pit/Status.groovy
Executable file
@ -0,0 +1,41 @@
|
||||
package com.jdbernard.pit
|
||||
|
||||
public enum Status {
|
||||
REASSIGNED('a'),
|
||||
REJECTED('j'),
|
||||
NEW('n'),
|
||||
RESOLVED('s'),
|
||||
VALIDATION_REQUIRED('v')
|
||||
|
||||
String symbol
|
||||
|
||||
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())) {
|
||||
|
||||
if (retVal != null)
|
||||
throw new IllegalArgumentException("Request string is" +
|
||||
" ambigous, '${str}' could represent ${retVal} or " +
|
||||
"${status}, possibly others.")
|
||||
|
||||
retVal = status
|
||||
}
|
||||
}
|
||||
|
||||
if (retVal == null)
|
||||
throw new IllegalArgumentException("No status matches '${str}'")
|
||||
|
||||
return retVal
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
def words = name().split("_")
|
||||
String result = ""
|
||||
words.each { result += "${it[0]}${it[1..-1].toLowerCase()} " }
|
||||
return result[0..-2]
|
||||
}
|
||||
}
|
213
libpit/src/main/com/jdbernard/pit/file/FileIssue.groovy
Executable file
213
libpit/src/main/com/jdbernard/pit/file/FileIssue.groovy
Executable file
@ -0,0 +1,213 @@
|
||||
package com.jdbernard.pit.file
|
||||
|
||||
import com.jdbernard.pit.*
|
||||
|
||||
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 {
|
||||
|
||||
protected File source
|
||||
private Logger log = LoggerFactory.getLogger(getClass())
|
||||
|
||||
public static final String fileExp = /(\d+)([bft])([ajnsv])(\d).*/
|
||||
|
||||
public FileIssue(File file) {
|
||||
|
||||
super(id: -1, title: 'REPLACE_ME')
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("Loading a FileIssue from '{}'", file.canonicalPath) }
|
||||
|
||||
def matcher = file.name =~ fileExp
|
||||
if (!matcher)
|
||||
throw new IllegalArgumentException("${file} " +
|
||||
"is not a valid Issue file.")
|
||||
|
||||
// Read issue attributes from the filename.
|
||||
super.@id = matcher[0][1]
|
||||
super.@category = Category.toCategory(matcher[0][2])
|
||||
super.@status = Status.toStatus(matcher[0][3])
|
||||
super.@priority = matcher[0][4].toInteger()
|
||||
|
||||
log.debug("id: {}\tcategory: {}\tstatus: {}\tpriority: {}",
|
||||
[super.@id, super.@category, super.@status, super.@priority])
|
||||
|
||||
this.source = file
|
||||
|
||||
String text = ""
|
||||
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 {
|
||||
boolean renamed
|
||||
renamed = source.renameTo(new File(source.canonicalFile.parentFile,
|
||||
makeFilename(id, c, status, priority)))
|
||||
|
||||
if (!renamed)
|
||||
throw new IOException("I was unable to set the category. "
|
||||
+ "I need to rename the file for this issue, but something is "
|
||||
+ "preventing me from doing so (maybe the path to the file is "
|
||||
+ "no longer valid, or maybe the file is currently open in "
|
||||
+ "some other program).")
|
||||
else super.setCategory(c)
|
||||
}
|
||||
|
||||
public void setStatus(Status s) throws IOException {
|
||||
boolean renamed
|
||||
renamed = source.renameTo(new File(source.canonicalFile.parentFile,
|
||||
makeFilename(id, category, s, priority)))
|
||||
|
||||
if (!renamed)
|
||||
throw new IOException("I was unable to set the status. "
|
||||
+ "I need to rename the file for this issue, but something is "
|
||||
+ "preventing me from doing so (maybe the path to the file is "
|
||||
+ "no longer valid, or maybe the file is currently open in "
|
||||
+ "some other program).")
|
||||
else super.setStatus(s)
|
||||
}
|
||||
|
||||
public void setPriority(int p) throws IOException {
|
||||
boolean renamed
|
||||
renamed = source.renameTo(new File(source.canonicalFile.parentFile,
|
||||
makeFilename(id, category, status, p)))
|
||||
|
||||
if (!renamed)
|
||||
throw new IOException("I was unable to set the priority. "
|
||||
+ "I need to rename the file for this issue, but something is "
|
||||
+ "preventing me from doing so (maybe the path to the file is "
|
||||
+ "no longer valid, or maybe the file is currently open in "
|
||||
+ "some other program).")
|
||||
else super.setPriority(p)
|
||||
}
|
||||
|
||||
public String getFilename() {
|
||||
return makeFilename(id, category, status, priority)
|
||||
}
|
||||
|
||||
public void setTitle(String title) throws IOException {
|
||||
super.setTitle(title)
|
||||
writeFile()
|
||||
}
|
||||
|
||||
public void setText(String text) throws IOException {
|
||||
super.setText(text)
|
||||
writeFile()
|
||||
}
|
||||
|
||||
boolean deleteFile() { return source.deleteDir() }
|
||||
|
||||
public static boolean isValidFilename(String name) {
|
||||
return name ==~ fileExp
|
||||
}
|
||||
|
||||
public static String makeFilename(String id, Category category,
|
||||
Status status, int priority) {
|
||||
|
||||
// bounds check priority
|
||||
priority = Math.min(9, Math.max(0, priority))
|
||||
|
||||
//check for valid values of cateogry and id
|
||||
if (category == null)
|
||||
throw new IAE("Category must be non-null.")
|
||||
if (status == null)
|
||||
throw new IAE("Status must be non-null.")
|
||||
if (!(id ==~ /\d+/))
|
||||
throw new IAE( "'${id}' is not a legal value for id.")
|
||||
|
||||
return id + category.symbol + status.symbol + priority + ".rst";
|
||||
}
|
||||
|
||||
public static String formatIssue(Issue issue) {
|
||||
def result = new StringBuilder()
|
||||
result.append(issue.@title)
|
||||
result.append("\n")
|
||||
result.append("=".multiply(issue.@title.length()))
|
||||
result.append("\n")
|
||||
result.append(issue.@text)
|
||||
result.append("\n----\n")
|
||||
|
||||
// If there are any extended properties, let's write those.
|
||||
if (issue.@extendedProperties.size() > 0) {
|
||||
def extOutput = [:]
|
||||
def maxKeyLen = 0
|
||||
def maxValLen = 0
|
||||
|
||||
// Find the longest key and value, convert all to strings.
|
||||
issue.@extendedProperties.each { key, val ->
|
||||
def ks = key.toString(), vs = formatProperty(val)
|
||||
extOutput[ks] = vs
|
||||
if (ks.length() > maxKeyLen) { maxKeyLen = ks.length() }
|
||||
if (vs.length() > maxKeyLen) { maxValLen = vs.length() } }
|
||||
|
||||
result.append("=".multiply(maxKeyLen + 1))
|
||||
result.append(" ")
|
||||
result.append("=".multiply(maxValLen))
|
||||
result.append("\n")
|
||||
|
||||
extOutput.sort().each { key, val ->
|
||||
result.append(key.padRight(maxKeyLen))
|
||||
result.append(": ")
|
||||
result.append(val.padRight(maxValLen))
|
||||
result.append("\n") }
|
||||
|
||||
result.append("=".multiply(maxKeyLen + 1))
|
||||
result.append(" ")
|
||||
result.append("=".multiply(maxValLen))
|
||||
result.append("\n") }}
|
||||
|
||||
protected void writeFile() {
|
||||
try { source.write(formatIssue(this)) }
|
||||
catch (IOException ioe) {
|
||||
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"
|
||||
+ " know why, I am sorry (maybe the file can not be reached).")
|
||||
}
|
||||
}
|
||||
|
||||
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") }
|
||||
}
|
100
libpit/src/main/com/jdbernard/pit/file/FileProject.groovy
Executable file
100
libpit/src/main/com/jdbernard/pit/file/FileProject.groovy
Executable file
@ -0,0 +1,100 @@
|
||||
package com.jdbernard.pit.file
|
||||
|
||||
import com.jdbernard.pit.*
|
||||
|
||||
class FileProject extends Project {
|
||||
|
||||
protected File source
|
||||
|
||||
public FileProject(File dir) {
|
||||
super(dir.canonicalFile.name)
|
||||
|
||||
if (!dir.isDirectory())
|
||||
throw new IllegalArgumentException(
|
||||
"${dir.name} is not a directory.")
|
||||
|
||||
this.source = dir
|
||||
|
||||
dir.eachFile { child ->
|
||||
|
||||
// add sub projects
|
||||
if (child.isDirectory()) {
|
||||
if (child.name ==~ /\d+/ ||
|
||||
child.isHidden()) return // just an issue folder
|
||||
|
||||
// otherwise build and add to list
|
||||
projects[(child.name)] = new FileProject(child)
|
||||
} else if (child.isFile() &&
|
||||
FileIssue.isValidFilename(child.name)) {
|
||||
def issue
|
||||
|
||||
// if exception, then not an issue
|
||||
try { issue = new FileIssue(child) } catch (all) { return }
|
||||
|
||||
issues[(issue.id)] = issue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
super.setName(name)
|
||||
source.renameTo(new File(source.canonicalFile.parentFile, name))
|
||||
}
|
||||
|
||||
public FileIssue createNewIssue(Map options) {
|
||||
if (!options) options = [:]
|
||||
if (!options.category) options.category = Category.TASK
|
||||
if (!options.status) options.status = Status.NEW
|
||||
if (!options.priority) options.priority = 5
|
||||
if (!options.text) options.text = "Default issue title.\n" +
|
||||
"====================\n"
|
||||
String id
|
||||
if (issues.size() == 0) id = '0000'
|
||||
else {
|
||||
id = (issues.values().max { it.id.toInteger() }).id
|
||||
id = (id.toInteger() + 1).toString().padLeft(id.length(), '0')
|
||||
}
|
||||
|
||||
def issueFile = new File(source, FileIssue.makeFilename(id,
|
||||
options.category, options.status, options.priority))
|
||||
|
||||
issueFile.createNewFile()
|
||||
issueFile.write(options.text)
|
||||
|
||||
def issue = new FileIssue(issueFile)
|
||||
issues[(issue.id)] = issue
|
||||
|
||||
return issue
|
||||
}
|
||||
|
||||
public FileProject createNewProject(String name) {
|
||||
def newDir = new File(source, name)
|
||||
newDir.mkdirs()
|
||||
|
||||
return new FileProject(newDir)
|
||||
}
|
||||
|
||||
public boolean deleteIssue(Issue issue) {
|
||||
if (!issues[(issue.id)]) return false
|
||||
|
||||
issues.remove(issue.id)
|
||||
if (issue instanceof FileIssue)
|
||||
return issue.deleteFile()
|
||||
|
||||
else return true
|
||||
}
|
||||
|
||||
public boolean deleteProject(Project project) {
|
||||
if (!projects[(project.name)]) return false
|
||||
|
||||
projects.remove(project.name)
|
||||
if (project instanceof FileProject)
|
||||
return project.source.delete()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return name }
|
||||
|
||||
}
|
22
libpit/src/main/com/jdbernard/pit/file/FileRepository.groovy
Executable file
22
libpit/src/main/com/jdbernard/pit/file/FileRepository.groovy
Executable file
@ -0,0 +1,22 @@
|
||||
package com.jdbernard.pit.file
|
||||
|
||||
import com.jdbernard.pit.*
|
||||
|
||||
public class FileRepository extends Repository {
|
||||
|
||||
@Delegate FileProject fileProject
|
||||
|
||||
public FileRepository(File dir) {
|
||||
assert dir.isDirectory()
|
||||
fileProject = new FileProject(dir)
|
||||
}
|
||||
|
||||
public void persist() {} // nothing to do
|
||||
public Project[] getRootProjects() {
|
||||
return [fileProject] as Project[]
|
||||
}
|
||||
|
||||
public FileProject createNewProject(String name) {
|
||||
return fileProject.createNewProject()
|
||||
}
|
||||
}
|
36
libpit/src/main/com/jdbernard/pit/util/Convert1_2.groovy
Executable file
36
libpit/src/main/com/jdbernard/pit/util/Convert1_2.groovy
Executable file
@ -0,0 +1,36 @@
|
||||
package com.jdbernard.pit.util
|
||||
|
||||
import com.jdbernard.pit.*
|
||||
|
||||
if (args.size() != 1) {
|
||||
println "Usage: Convert1_2 [dir]"
|
||||
System.exit(1)
|
||||
}
|
||||
|
||||
File rootDir = new File(args[0])
|
||||
Scanner scan = new Scanner(System.in)
|
||||
|
||||
rootDir.eachFileRecurse { file ->
|
||||
def m = file.name =~ /(\d+)([bcft])(\d).*/
|
||||
if (m && file.isFile()) {
|
||||
println m[0][0]
|
||||
def parentFile = file.canonicalFile.parentFile
|
||||
def c
|
||||
def s
|
||||
switch(m[0][2]) {
|
||||
case "c":
|
||||
println file.readLines()[0]
|
||||
print "Issue was closed, was category does it belong in?"
|
||||
c = Category.toCategory(scan.nextLine())
|
||||
s = Status.RESOLVED
|
||||
break
|
||||
default:
|
||||
c = Category.toCategory(m[0][2])
|
||||
s = Status.NEW
|
||||
break
|
||||
}
|
||||
println "${m[0][2]}: ${c}"
|
||||
file.renameTo(new File(parentFile,
|
||||
FileIssue.makeFilename(m[0][1], c, s, m[0][3].toInteger())))
|
||||
}
|
||||
}
|
74
libpit/src/main/com/jdbernard/pit/xml/XmlIssue.groovy
Executable file
74
libpit/src/main/com/jdbernard/pit/xml/XmlIssue.groovy
Executable file
@ -0,0 +1,74 @@
|
||||
package com.jdbernard.pit.xml
|
||||
|
||||
import com.jdbernard.pit.*
|
||||
|
||||
public class XmlIssue extends Issue {
|
||||
|
||||
def issueNode
|
||||
XmlProject project
|
||||
XmlRepository repository
|
||||
|
||||
XmlIssue(def issueNode, XmlRepository repository, XmlProject project) {
|
||||
super(issueNode.@id, issueNode.@category ?: Category.TASK,
|
||||
issueNode.@status ?: Status.NEW, issueNode.@priority ?: 9)
|
||||
|
||||
this.issueNode = issueNode
|
||||
this.project = project
|
||||
this.repository = repository
|
||||
}
|
||||
|
||||
XmlIssue(String id, Category c = Category.TASK, Status s = Status.NEW,
|
||||
int p = 9, String title, String text, XmlRepository repository,
|
||||
XmlProject project) {
|
||||
super(id, c, s, p)
|
||||
|
||||
this.project = project
|
||||
this.repository = repository
|
||||
|
||||
// Node constructor adds the node to the parent node
|
||||
issueNode = new Node(project.projectNode, "Issue",
|
||||
[id: id, category: c, status: s, priority: p, title: title])
|
||||
|
||||
super.@title = title
|
||||
super.@text = text
|
||||
issueNode.value = text
|
||||
|
||||
repository.persist()
|
||||
}
|
||||
|
||||
public void setCategory(Category c) {
|
||||
super.setCategory(c)
|
||||
|
||||
issueNode.@category = c.name()
|
||||
repository.persist()
|
||||
}
|
||||
|
||||
public void setStatus(Status s) {
|
||||
super.setStatus(s)
|
||||
|
||||
issueNode.@status = s.name()
|
||||
repository.persist()
|
||||
}
|
||||
|
||||
public void setPriority(int p) {
|
||||
super.setPriority(p)
|
||||
|
||||
issueNode.@priority = p
|
||||
repository.persist()
|
||||
}
|
||||
|
||||
public void setText(String t) {
|
||||
super.setText(t)
|
||||
|
||||
issueNode.value = t
|
||||
repository.persist()
|
||||
}
|
||||
|
||||
public void setTitle(String t) {
|
||||
super.setTitle(t)
|
||||
|
||||
issueNode.@title = t
|
||||
repository.persist()
|
||||
}
|
||||
|
||||
}
|
83
libpit/src/main/com/jdbernard/pit/xml/XmlProject.groovy
Executable file
83
libpit/src/main/com/jdbernard/pit/xml/XmlProject.groovy
Executable file
@ -0,0 +1,83 @@
|
||||
package com.jdbernard.pit.xml
|
||||
|
||||
import com.jdbernard.pit.*
|
||||
|
||||
public class XmlProject extends Project {
|
||||
|
||||
def projectNode
|
||||
XmlRepository repository
|
||||
|
||||
XmlProject(def projectNode, XmlRepository repository) {
|
||||
super(projectNode.@name)
|
||||
|
||||
this.projectNode = projectNode
|
||||
this.repository = repository
|
||||
}
|
||||
|
||||
XmlProject(String name, def parentProject, XmlRepository repository) {
|
||||
super(name)
|
||||
|
||||
// Node constructor adds the node to the parent node
|
||||
projectNode = new Node(parentProject.projectNode, "Project",
|
||||
[name: name])
|
||||
|
||||
repository.persist()
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
super.setName(name)
|
||||
|
||||
projectNode.@name = name
|
||||
repository.persist()
|
||||
}
|
||||
|
||||
public XmlIssue createNewIssue(Map options) {
|
||||
if (!options) options = [:]
|
||||
if (!options.category) options.category = Category.TASK
|
||||
if (!options.status) options.status = Status.NEW
|
||||
if (!options.priority) options.priority = 5
|
||||
if (!options.text) options.text = "Default issue title.\n" +
|
||||
"====================\n"
|
||||
|
||||
String id
|
||||
if (issues.size() == 0) id = "0000"
|
||||
else {
|
||||
id = (issues.values().max { it.id.toInteger() }).id
|
||||
id = (id.toInteger() + 1).toString().padLeft(id.length(), '0')
|
||||
}
|
||||
|
||||
// XmlIssue constructor will persist XML data
|
||||
issues[(id)] = new XmlIssue(id, options.category, options.status,
|
||||
options.priority, options.text, repository, this)
|
||||
|
||||
return issues[(id)]
|
||||
}
|
||||
|
||||
public XmlProject createNewProject(String name) {
|
||||
// XmlProject constructor persists the XML data
|
||||
projects[(name)] = new XmlProject(name, this, repository)
|
||||
return projects[(name)]
|
||||
}
|
||||
|
||||
public boolean deleteIssue(Issue issue) {
|
||||
if (!issues[(issue.id)]) return false
|
||||
|
||||
issues.remove(issue.id)
|
||||
if (issue instanceof XmlIssue)
|
||||
projectNode.remove(issue.issueNode)
|
||||
|
||||
repository.persist()
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
public boolean deleteProject(Project project) {
|
||||
if (!projects[(project.name)]) return false
|
||||
|
||||
projects.remove(project.name)
|
||||
if (project instanceof XmlProject)
|
||||
projectNode.remove(project.projectNode)
|
||||
|
||||
repository.persist()
|
||||
}
|
||||
}
|
47
libpit/src/main/com/jdbernard/pit/xml/XmlRepository.groovy
Executable file
47
libpit/src/main/com/jdbernard/pit/xml/XmlRepository.groovy
Executable file
@ -0,0 +1,47 @@
|
||||
package com.jdbernard.pit.xml
|
||||
|
||||
import com.jdbernard.pit.*
|
||||
import groovy.xml.XmlUtil
|
||||
|
||||
public class XmlRepository extends Repository {
|
||||
|
||||
def repository
|
||||
def projects = []
|
||||
File repoFile
|
||||
|
||||
public XmlRepository(File repoFile) {
|
||||
|
||||
this.repoFile = repoFile
|
||||
repository = new XmlParser().parse(repoFile)
|
||||
|
||||
repository.Project.each { projectNode ->
|
||||
projects << new XmlProject(projectNode)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public synchronized void persist() {
|
||||
repoFile.withOutputStream { XmlUtil.serialize(repository, it) }
|
||||
}
|
||||
|
||||
public Project[] getRootProjects() {
|
||||
return projects as XmlProject[]
|
||||
}
|
||||
|
||||
public XmlProject createNewProject(String name) {
|
||||
def newProject = new XmlProject(name, this, null)
|
||||
repository << newProject.projectNode
|
||||
|
||||
persist()
|
||||
return newProject
|
||||
}
|
||||
|
||||
public boolean deleteProject(Project p) {
|
||||
if (!projects.contains(p)) return false
|
||||
|
||||
projects.remove(p)
|
||||
repository.remove(p.projectNode)
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user