Task: Simple database




Go: package main

import (
    "encoding/json"
    "fmt"
    "io"
    "os"
    "sort"
    "strings"
    "time"
    "unicode"
)



type Item struct {
    Stamp time.Time
    Name  string
    Tags  []string `json:",omitempty"`
    Notes string   `json:",omitempty"`
}


func (i *Item) String() string {
    s := i.Stamp.Format(time.ANSIC) + "\n  Name:  " + i.Name
    if len(i.Tags) > 0 {
        s = fmt.Sprintf("%s\n  Tags:  %v", s, i.Tags)
    }
    if i.Notes > "" {
        s += "\n  Notes: " + i.Notes
    }
    return s
}


type db []*Item


func (d db) Len() int           { return len(d) }
func (d db) Swap(i, j int)      { d[i], d[j] = d[j], d[i] }
func (d db) Less(i, j int) bool { return d[i].Stamp.Before(d[j].Stamp) }


const fn = "sdb.json"

func main() {
    if len(os.Args) == 1 {
        latest()
        return
    }
    switch os.Args[1] {
    case "add":
        add()
    case "latest":
        latest()
    case "tags":
        tags()
    case "all":
        all()
    case "help":
        help()
    default:
        usage("unrecognized command")
    }
}

func usage(err string) {
    if err > "" {
        fmt.Println(err)
    }
    fmt.Println(`usage:  sdb [command] [data]
    where command is one of add, latest, tags, all, or help.`)
}

func help() {
    usage("")
    fmt.Println(`
Commands must be in lower case.
If no command is specified, the default command is latest.

Latest prints the latest item.
All prints all items in chronological order.
Tags prints the lastest item for each tag.
Help prints this message.

Add adds data as a new record.  The format is,

  name [tags] [notes]

Name is the name of the item and is required for the add command.

Tags are optional.  A tag is a single word.
A single tag can be specified without enclosing brackets.
Multiple tags can be specified by enclosing them in square brackets.

Text remaining after tags is taken as notes.  Notes do not have to be
enclosed in quotes or brackets.  The brackets above are only showing
that notes are optional.

Quotes may be useful however--as recognized by your operating system shell
or command line--to allow entry of arbitrary text.  In particular, quotes
or escape characters may be needed to prevent the shell from trying to
interpret brackets or other special characters.

Examples:
sdb add Bookends                        
sdb add Bookends rock my favorite       
sdb add Bookends [rock folk]            
sdb add Bookends [] "Simon & Garfunkel" 
sdb add "Simon&Garfunkel [artist]"      
    
As shown in the last example, if you use features of your shell to pass
all data as a single string, the item name and tags will still be identified
by separating whitespace.
    
The database is stored in JSON format in the file "sdb.json"
`)  
}


func load() (db, bool) {
    d, f, ok := open()
    if ok {
        f.Close()
        if len(d) == 0 {
            fmt.Println("no items")
            ok = false
        }
    }
    return d, ok
}


func open() (d db, f *os.File, ok bool) {
    var err error
    f, err = os.OpenFile(fn, os.O_RDWR|os.O_CREATE, 0666)
    if err != nil {
        fmt.Println("cant open??")
        fmt.Println(err)
        return
    }
    jd := json.NewDecoder(f)
    err = jd.Decode(&d)
    
    if err != nil && err != io.EOF {
        fmt.Println(err)
        f.Close()
        return
    }
    ok = true
    return
}


func latest() {
    d, ok := load()
    if !ok {
        return
    }
    sort.Sort(d)
    fmt.Println(d[len(d)-1])
}


func all() {
    d, ok := load()
    if !ok {
        return
    }
    sort.Sort(d)
    for _, i := range d {
        fmt.Println("-----------------------------------")
        fmt.Println(i)
    }
    fmt.Println("-----------------------------------")
}


func tags() {
    d, ok := load()
    if !ok {
        return
    }
    
    
    
    latest := make(map[string]*Item)
    for _, item := range d {
        for _, tag := range item.Tags {
            li, ok := latest[tag]
            if !ok || item.Stamp.After(li.Stamp) {
                latest[tag] = item
            }
        }
    }
    
    
    type itemTags struct {
        item *Item
        tags []string
    }
    inv := make(map[*Item][]string)
    for tag, item := range latest {
        inv[item] = append(inv[item], tag)
    }
    
    li := make(db, len(inv))
    i := 0
    for item := range inv {
        li[i] = item
        i++
    }
    sort.Sort(li)
    
    for _, item := range li {
        tags := inv[item]
        fmt.Println("-----------------------------------")
        fmt.Println("Latest item with tags", tags)
        fmt.Println(item)
    }
    fmt.Println("-----------------------------------")
}
    

func add() { 
    if len(os.Args) < 3 {
        usage("add command requires data")
        return
    } else if len(os.Args) == 3 {
        add1()
    } else {
        add4()
    }
}   


func add1() {
    data := strings.TrimLeftFunc(os.Args[2], unicode.IsSpace)
    if data == "" {
        
        usage("invalid name")
        return 
    }
    sep := strings.IndexFunc(data, unicode.IsSpace)
    if sep < 0 {
        
        addItem(data, nil, "")
        return
    }
    name := data[:sep]
    data = strings.TrimLeftFunc(data[sep:], unicode.IsSpace)
    if data == "" {
        
        addItem(name, nil, "")
        return
    }
    if data[0] == '[' {
        sep = strings.Index(data, "]")
        if sep < 0 {
            
            addItem(name, strings.Fields(data[1:]), "")
        } else {
            
            addItem(name, strings.Fields(data[1:sep]),
                strings.TrimLeftFunc(data[sep+1:], unicode.IsSpace))
        }
        return
    }
    sep = strings.IndexFunc(data, unicode.IsSpace)
    if sep < 0 {
        
        addItem(name, []string{data}, "")
    } else {
        
        addItem(name, []string{data[:sep]},
            strings.TrimLeftFunc(data[sep+1:], unicode.IsSpace))
    }
}


func add4() {
    name := os.Args[2]
    tag1 := os.Args[3]
    if tag1[0] != '[' {
        
        addItem(name, []string{tag1}, strings.Join(os.Args[4:], " "))
        return
    }
    if tag1[len(tag1)-1] == ']' {
        
        addItem(name, strings.Fields(tag1[1:len(tag1)-1]),
            strings.Join(os.Args[4:], " "))
        return
    }
    
    var tags []string
    if tag1 > "[" {
        tags = []string{tag1[1:]}
    }
    for x, tag := range os.Args[4:] {
        if tag[len(tag)-1] != ']' {
            tags = append(tags, tag)
        } else {
            
            if tag > "]" {
                tags = append(tags, tag[:len(tag)-1])
            }
            addItem(name, tags, strings.Join(os.Args[5+x:], " "))
            return
        }
    }
    
    addItem(name, tags, "")
}


func addItem(name string, tags []string, notes string) {
    db, f, ok := open()
    if !ok {
        return
    }
    defer f.Close()
    
    db = append(db, &Item{time.Now(), name, tags, notes})
    sort.Sort(db)
    js, err := json.MarshalIndent(db, "", "  ")
    if err != nil {
        fmt.Println(err)
        return
    }
    
    if _, err = f.Seek(0, 0); err != nil {
        fmt.Println(err)
        return
    }
    f.Truncate(0)
    if _, err = f.Write(js); err != nil {
        fmt.Println(err)
    }
}


Java: import java.io.*;
import java.text.*;
import java.util.*;

public class SimpleDatabase {

    final static String filename = "simdb.csv";

    public static void main(String[] args) {
        if (args.length < 1 || args.length > 3) {
            printUsage();
            return;
        }

        switch (args[0].toLowerCase()) {
            case "add":
                addItem(args);
                break;
            case "latest":
                printLatest(args);
                break;
            case "all":
                printAll();
                break;
            default:
                printUsage();
                break;
        }
    }

    private static class Item implements Comparable<Item>{
        final String name;
        final String date;
        final String category;

        Item(String n, String d, String c) {
            name = n;
            date = d;
            category = c;
        }

        @Override
        public int compareTo(Item item){
            return date.compareTo(item.date);
        }

        @Override
        public String toString() {
            return String.format("%s,%s,%s%n", name, date, category);
        }
    }

    private static void addItem(String[] input) {
        if (input.length < 2) {
            printUsage();
            return;
        }
        List<Item> db = load();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String date = sdf.format(new Date());
        String cat = (input.length == 3) ? input[2] : "none";
        db.add(new Item(input[1], date, cat));
        store(db);
    }

    private static void printLatest(String[] a) {
        List<Item> db = load();
        if (db.isEmpty()) {
            System.out.println("No entries in database.");
            return;
        }
        Collections.sort(db);
        if (a.length == 2) {
            for (Item item : db)
                if (item.category.equals(a[1]))
                    System.out.println(item);
        } else {
            System.out.println(db.get(0));
        }
    }

    private static void printAll() {
        List<Item> db = load();
        if (db.isEmpty()) {
            System.out.println("No entries in database.");
            return;
        }
        Collections.sort(db);
        for (Item item : db)
            System.out.println(item);
    }

    private static List<Item> load() {
        List<Item> db = new ArrayList<>();
        try (Scanner sc = new Scanner(new File(filename))) {
            while (sc.hasNext()) {
                String[] item = sc.nextLine().split(",");
                db.add(new Item(item[0], item[1], item[2]));
            }
        } catch (IOException e) {
            System.out.println(e);
        }
        return db;
    }

    private static void store(List<Item> db) {
        try (FileWriter fw = new FileWriter(filename)) {
            for (Item item : db)
                fw.write(item.toString());
        } catch (IOException e) {
            System.out.println(e);
        }
    }

    private static void printUsage() {
         System.out.println("Usage:");
         System.out.println("  simdb cmd [categoryName]");
         System.out.println("  add     add item, followed by optional category");
         System.out.println("  latest  print last added item(s), followed by "
                 + "optional category");
         System.out.println("  all     print all");
         System.out.println("  For instance: add \"some item name\" "
                 + "\"some category name\"");
    }
}