// returns a copy of a taxon func (tx *taxon) copy() *jdh.Taxon { tax := &jdh.Taxon{ Id: strings.TrimSpace(tx.id), Name: strings.Join(strings.Fields(tx.name), " "), Rank: jdh.GetRank(strings.TrimSpace(tx.rank)), } return tax }
func spInRun(c *cmdapp.Command, args []string) { openLocal(c) pId := "" if len(ancFlag) > 0 { p := taxon(c, localDB, ancFlag) if !p.IsValid { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("taxon "+p.Name+" a synonym, can ot be a parent")) os.Exit(1) } pId = p.Id } var set *jdh.Dataset if len(dsetFlag) > 0 { set = dataset(c, localDB, dsetFlag) } else { set = &jdh.Dataset{} } rank := jdh.Unranked if len(rankFlag) > 0 { rank = jdh.GetRank(rankFlag) } format := "txt" if len(formatFlag) > 0 { format = formatFlag } if len(args) > 0 { switch format { case "txt": for _, fname := range args { spInTxt(c, fname, pId, rank, set) } case "ndm": for _, fname := range args { spInNdm(c, fname, pId, rank, set) } default: fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("format "+format+" unknown")) os.Exit(1) } } else { switch format { case "txt": spInTxt(c, "", pId, rank, set) case "ndm": spInNdm(c, "", pId, rank, set) default: fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("format "+format+" unknown")) os.Exit(1) } } localDB.Exec(jdh.Commit, "", nil) }
// TrInPickTax returns the id of a given taxon name. If there are more taxons // fullfilling the name, then it will print a list of the potential // names and finish the program. func trInPickTax(name, parent string) (string, error) { vals := new(jdh.Values) vals.Add(jdh.TaxName, name) if len(parent) != 0 { vals.Add(jdh.TaxParent, parent) } rank := jdh.Unranked if len(rankFlag) > 0 { rank = jdh.GetRank(rankFlag) if rank != jdh.Unranked { vals.Add(jdh.TaxRank, rankFlag) } } l, err := localDB.List(jdh.Taxonomy, vals) if err != nil { return "", err } var tax *jdh.Taxon mult := "" for { ot := &jdh.Taxon{} if err := l.Scan(ot); err != nil { if err == io.EOF { break } return "", err } if tax == nil { tax = ot continue } if len(mult) == 0 { mult = fmt.Sprintf("ambiguos taxon name\n") mult += fmt.Sprintf("%s\t%s\n", tax.Id, tax.Name) } mult += fmt.Sprintf("%s\t%s\n", ot.Id, ot.Name) } if len(mult) > 0 { return "", errors.New(mult) } if tax == nil { tax = &jdh.Taxon{ Name: name, IsValid: true, Parent: parent, Rank: rank, } return localDB.Exec(jdh.Add, jdh.Taxonomy, tax) } return tax.Id, nil }
func raMkRun(c *cmdapp.Command, args []string) { openLocal(c) var spDB jdh.DB if len(extDBFlag) > 0 { openExt(c, extDBFlag, "") spDB = extDB } else { spDB = localDB } var tax *jdh.Taxon if len(taxonFlag) > 0 { tax = taxon(c, localDB, taxonFlag) if len(tax.Id) == 0 { return } } else if len(args) > 0 { if len(args) > 2 { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("too many arguments")) os.Exit(1) } pName := "" if len(args) > 1 { pName = args[1] } tax = pickTaxName(c, localDB, args[0], pName) if len(tax.Id) == 0 { return } } else { tax = &jdh.Taxon{} } rank := jdh.Kingdom if len(rankFlag) > 0 { rank = jdh.GetRank(rankFlag) if rank == jdh.Unranked { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("invalid rank")) os.Exit(1) } } if (sizeFlag <= 0) || (sizeFlag >= 360) { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("invalid size option")) os.Exit(1) } cols := uint(360 / sizeFlag) if int(float64(cols)*sizeFlag) < 360 { cols++ } size := 360 / float64(cols) raMkFetch(c, spDB, tax, jdh.Kingdom, rank, size) localDB.Exec(jdh.Commit, "", nil) }
func txInRun(c *cmdapp.Command, args []string) { openLocal(c) pId := "" if len(ancFlag) > 0 { p := taxon(c, localDB, ancFlag) if !p.IsValid { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("taxon "+p.Name+" a synonym, can ot be a parent")) os.Exit(1) } pId = p.Id } valid := true if synonymFlag { if len(pId) == 0 { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("synonym without defined parent")) os.Exit(2) } valid = false } rank := jdh.Unranked if len(rankFlag) > 0 { rank = jdh.GetRank(rankFlag) } format := "txt" if len(formatFlag) > 0 { format = formatFlag } if len(args) > 0 { switch format { case "txt": for _, fn := range args { txInTxt(c, fn, pId, rank, valid) } default: fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("format "+format+" unknown")) os.Exit(1) } } else { switch format { case "txt": txInTxt(c, "", pId, rank, valid) default: fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("format "+format+" unknown")) os.Exit(1) } } localDB.Exec(jdh.Commit, "", nil) }
// returns a copy of species func (sp *species) copy() *jdh.Taxon { if sp.Key == 0 { return &jdh.Taxon{} } rank := jdh.GetRank(strings.TrimSpace(sp.Rank)) tax := &jdh.Taxon{ Id: strconv.FormatInt(sp.Key, 10), Name: strings.Join(strings.Fields(sp.CanonicalName), " "), Authority: strings.Join(strings.Fields(sp.Authorship), " "), Rank: rank, IsValid: !sp.Synonym, Comment: strings.Join(strings.Fields(sp.AccordingTo), " "), } if sp.Synonym { tax.Parent = strconv.FormatInt(sp.AcceptedKey, 10) } else if sp.ParentKey > 0 { tax.Parent = strconv.FormatInt(sp.ParentKey, 10) } return tax }
func spPopRun(c *cmdapp.Command, args []string) { if len(extDBFlag) == 0 { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("expectiong '--extdb' option")) c.Usage() } openLocal(c) openExt(c, extDBFlag, "") var tax *jdh.Taxon if len(taxonFlag) > 0 { tax = taxon(c, localDB, taxonFlag) if len(tax.Id) == 0 { return } } else if len(args) > 0 { if len(args) > 2 { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("too many arguments")) os.Exit(1) } pName := "" if len(args) > 1 { pName = args[1] } tax = pickTaxName(c, localDB, args[0], pName) if len(tax.Id) == 0 { return } } else { tax = &jdh.Taxon{} } rank := jdh.Kingdom if len(rankFlag) > 0 { rank = jdh.GetRank(rankFlag) if rank == jdh.Unranked { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("invalid rank")) os.Exit(1) } } spPopFetch(c, tax, jdh.Kingdom, rank) localDB.Exec(jdh.Commit, "", nil) }
// searchTaxon searchs for taxon name in gbif. func (db *DB) searchTaxon(l *listScanner, name string, kvs []jdh.KeyValue) { nm := strings.Join(strings.Fields(name), " ") if i := strings.Index(nm, "*"); i > 0 { l.setErr(errors.New("current gbif api does not support partial lookups")) return } var pId int64 var pName string var rank jdh.Rank for _, kv := range kvs { if len(kv.Value) == 0 { continue } switch kv.Key { case jdh.TaxParent: pId, _ = strconv.ParseInt(kv.Value[0], 10, 64) case jdh.TaxRank: rank = jdh.GetRank(kv.Value[0]) case jdh.TaxParentName: pName = strings.ToLower(strings.Join(strings.Fields(kv.Value[0]), " ")) } } vals := url.Values{} vals.Add("name", nm) for off := int64(0); ; { if off > 0 { vals.Set("offset", strconv.FormatInt(off, 10)) } request := wsHead + "species?" + vals.Encode() an := new(spAnswer) if err := db.listRequest(request, an); err != nil { l.setErr(err) return } for _, sp := range an.Results { if sp.Key != sp.NubKey { continue } if pId != 0 { if !sp.isDesc(pId) { continue } } if rank != jdh.Unranked { if rank != jdh.GetRank(strings.TrimSpace(sp.Rank)) { continue } } if len(pName) > 0 { if !sp.hasParentName(pName) { continue } } select { case l.c <- sp.copy(): case <-l.end: return } } if an.EndOfRecords { break } off += an.Limit } select { case l.c <- nil: case <-l.end: } }
func readTaxon(id string) (*taxon, error) { if (len(id) == 0) || (id == "0") { return nil, errors.New("taxon without identification") } request := emblHead + "Taxon:" + id + "&display=xml" answer, err := http.Get(request) if err != nil { return nil, err } defer answer.Body.Close() dec := xml.NewDecoder(answer.Body) tx := &taxon{ tax: &jdh.Taxon{}, } for tk, err := dec.Token(); err != io.EOF; tk, err = dec.Token() { if err != nil { return nil, err } switch t := tk.(type) { case xml.StartElement: switch t.Name.Local { case "taxon": for _, at := range t.Attr { switch at.Name.Local { case "scientificName": tx.tax.Name = at.Value tx.tax.IsValid = true case "taxId": tx.tax.Id = at.Value case "parentTaxId": tx.tax.Parent = at.Value case "rank": tx.tax.Rank = jdh.GetRank(at.Value) } } case "lineage": if err = readTaxonLineage(dec, tx); err != nil { return nil, err } case "children": skip(dec, t.Name.Local) case "synonym": nm := "" isSyn := false for _, at := range t.Attr { switch at.Name.Local { case "type": if at.Value == "synonym" { isSyn = true } case "name": nm = at.Value } } if isSyn { tx.syn = append(tx.syn, nm) } } } } return tx, nil }
// SearchTaxon search for a taxon name in iNaturalist. func (db *DB) searchTaxon(l *listScanner, name string, kvs []jdh.KeyValue) { nm := strings.Join(strings.Fields(name), " ") i := strings.Index(nm, "*") prefix := "" if i == 0 { l.setErr(errors.New("taxon without identification")) return } if i > 0 { prefix = nm[:i] nm = nm[:i] } pId := "" var rank jdh.Rank pName := "" for _, kv := range kvs { if len(kv.Value) == 0 { continue } switch kv.Key { case jdh.TaxParent: pId = kv.Value[0] case jdh.TaxRank: rank = jdh.GetRank(kv.Value[0]) case jdh.TaxParentName: pName = strings.Join(strings.Fields(kv.Value[0]), " ") } } vals := url.Values{} vals.Add("q", name) vals.Add("utf8", "✓") for next := 1; ; next++ { if next > 1 { vals.Set("page", strconv.FormatInt(int64(next), 10)) } ls, nx, err := db.txSearch(inatHead + "taxa/search?" + vals.Encode()) if err != nil { l.setErr(err) return } for _, tx := range ls { tax := tx.copy() if len(prefix) > 0 { if strings.HasPrefix(tax.Name, prefix) { if len(pId) > 0 { pl, err := db.txList(tx.id) if err != nil { l.setErr(err) return } isP := false for _, pt := range pl { if pt.id == tx.id { break } if pt.id == pId { isP = true break } } if !isP { continue } } if len(pName) > 0 { pl, err := db.txList(tx.id) if err != nil { l.setErr(err) return } isP := false for _, pt := range pl { if pt.id == tx.id { break } if strings.Join(strings.Fields(pt.name), " ") == pName { isP = true break } } if !isP { continue } } if rank != jdh.Unranked { if rank != tax.Rank { continue } } select { case l.c <- tax: case <-l.end: return } } continue } if tax.Name == nm { if len(pId) > 0 { pl, err := db.txList(tx.id) if err != nil { l.setErr(err) return } isP := false for _, pt := range pl { if pt.id == tx.id { break } if pt.id == pId { isP = true break } } if !isP { continue } } if len(pName) > 0 { pl, err := db.txList(tx.id) if err != nil { l.setErr(err) return } isP := false for _, pt := range pl { if pt.id == tx.id { break } if strings.Join(strings.Fields(pt.name), " ") == pName { isP = true break } } if !isP { continue } } if rank != jdh.Unranked { if rank != tax.Rank { continue } } select { case l.c <- tax: case <-l.end: return } } } if !nx { break } } select { case l.c <- nil: case <-l.end: } }
// SearchTaxon search for a taxon name in ncbi. func (db *DB) searchTaxon(l *listScanner, name string, kvs []jdh.KeyValue) { nm := strings.Join(strings.Fields(name), " ") i := strings.Index(nm, "*") prefix := "" if i == 0 { l.setErr(errors.New("taxon without identification")) return } if i > 0 { prefix = nm[:i] nm = nm[:i] } pId := "" var rank jdh.Rank pName := "" for _, kv := range kvs { if len(kv.Value) == 0 { continue } switch kv.Key { case jdh.TaxParent: pId = kv.Value[0] case jdh.TaxRank: rank = jdh.GetRank(kv.Value[0]) case jdh.TaxParentName: pName = strings.Join(strings.Fields(kv.Value[0]), " ") } } vals := url.Values{} vals.Add("db", "taxonomy") vals.Add("term", nm) for next := 0; ; { if next > 0 { vals.Set("RetStart", strconv.FormatInt(int64(next), 10)) } request := ncbiHead + "esearch.fcgi?" + vals.Encode() nl, nx, err := db.search(request) if err != nil { l.setErr(err) return } for _, id := range nl { tx, err := readTaxon(id) if err != nil { l.setErr(err) return } if len(prefix) > 0 { if strings.HasPrefix(tx.tax.Name, prefix) { if len(pId) > 0 { isP := false for _, p := range tx.par { if pId == p { isP = true break } } if !isP { continue } } if rank != jdh.Unranked { if rank != tx.tax.Rank { continue } } if len(pName) > 0 { isP := false for _, p := range tx.lin { if pName == p { isP = true break } } if !isP { continue } } select { case l.c <- tx.tax: case <-l.end: return } continue } for i, s := range tx.syn { if strings.HasPrefix(s, prefix) { if len(pId) > 0 { isP := false for _, p := range tx.par { if pId == p { isP = true break } } if !isP { continue } } if rank != jdh.Unranked { if rank != tx.tax.Rank { continue } } if len(pName) > 0 { isP := false for _, p := range tx.lin { if pName == p { isP = true break } } if !isP { continue } } st := &jdh.Taxon{ Id: tx.tax.Id + "." + strconv.FormatInt(int64(i)+1, 10), Name: s, IsValid: false, Parent: tx.tax.Id, Rank: tx.tax.Rank, } select { case l.c <- st: case <-l.end: return } break } } continue } if tx.tax.Name == nm { if len(pId) > 0 { isP := false for _, p := range tx.par { if pId == p { isP = true break } } if !isP { continue } } if rank != jdh.Unranked { if rank != tx.tax.Rank { continue } } if len(pName) > 0 { isP := false for _, p := range tx.lin { if pName == p { isP = true break } } if !isP { continue } } select { case l.c <- tx.tax: case <-l.end: return } continue } for i, s := range tx.syn { if s == nm { if len(pId) > 0 { isP := false for _, p := range tx.par { if pId == p { isP = true break } } if !isP { continue } } if rank != jdh.Unranked { if rank != tx.tax.Rank { continue } } st := &jdh.Taxon{ Id: tx.tax.Id + "." + strconv.FormatInt(int64(i)+1, 10), Name: s, IsValid: false, Parent: tx.tax.Id, Rank: tx.tax.Rank, } select { case l.c <- st: case <-l.end: return } break } } } if nx == 0 { break } next = nx } select { case l.c <- nil: case <-l.end: } }
func txLsRun(c *cmdapp.Command, args []string) { var db jdh.DB if len(extDBFlag) != 0 { openExt(c, extDBFlag, "") db = extDB } else { openLocal(c) db = localDB } var tax *jdh.Taxon if len(idFlag) > 0 { tax = taxon(c, db, idFlag) if len(tax.Id) == 0 { return } } else if len(args) > 0 { if len(args) > 2 { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("too many arguments")) os.Exit(1) } pName := "" if len(args) > 1 { pName = args[1] } tax = pickTaxName(c, db, args[0], pName) if len(tax.Id) == 0 { return } } if ancsFlag { if tax == nil { os.Exit(0) } vals := new(jdh.Values) vals.Add(jdh.TaxParents, tax.Id) l, err := db.List(jdh.Taxonomy, vals) if err != nil { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr(err)) os.Exit(1) } txLsProc(c, l) return } if len(rankFlag) > 0 { rank := jdh.GetRank(rankFlag) if (rank == jdh.Unranked) && (strings.ToLower(rankFlag) != rank.String()) { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("unknown rank")) os.Exit(1) } txLsRank(c, db, tax, rank) return } if synonymFlag { if tax == nil { os.Exit(0) } l := getTaxDesc(c, db, tax.Id, false) txLsProc(c, l) return } id := "" if tax != nil { id = tax.Id } l := getTaxDesc(c, db, id, true) txLsProc(c, l) }
func txSyncRun(c *cmdapp.Command, args []string) { if len(extDBFlag) == 0 { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("expectiong '--extdb' option")) c.Usage() } openLocal(c) openExt(c, extDBFlag, "") var tax *jdh.Taxon if len(idFlag) > 0 { tax = taxon(c, localDB, idFlag) if len(tax.Id) == 0 { return } } else if len(args) > 0 { if len(args) > 2 { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("too many arguments")) os.Exit(1) } pName := "" if len(args) > 1 { pName = args[1] } tax = pickTaxName(c, localDB, args[0], pName) if len(tax.Id) == 0 { return } } else { tax = &jdh.Taxon{} } noFlag := true if matchFlag { txSyncMatch(c, tax) localDB.Exec(jdh.Commit, "", nil) noFlag = false } if updateFlag || validFlag { txSyncUpdate(c, tax) localDB.Exec(jdh.Commit, "", nil) noFlag = false } if len(rankFlag) > 0 { rank := jdh.GetRank(rankFlag) if rank == jdh.Unranked { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("invalid rank")) os.Exit(1) } txSyncRank(c, tax, rank) localDB.Exec(jdh.Commit, "", nil) noFlag = false } if len(popFlag) > 0 { rank := jdh.GetRank(popFlag) if rank == jdh.Unranked { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("invalid rank")) os.Exit(1) } prev := tax.Rank if prev == jdh.Unranked { prev = jdh.Kingdom } txSyncPop(c, tax, prev, rank) localDB.Exec(jdh.Commit, "", nil) noFlag = false } if noFlag { fmt.Fprintf(os.Stderr, "%s\n", c.ErrStr("undefined action")) c.Usage() } }
// Set sets a value of a taxon in the taxonomy. func (t *taxonomy) set(vals []jdh.KeyValue) error { id := "" for _, kv := range vals { if len(kv.Value) == 0 { continue } if kv.Key == jdh.KeyId { id = kv.Value[0] break } } if len(id) == 0 { return errors.New("taxon without identification") } tx, ok := t.ids[id] if !ok { return nil } tax := tx.data for _, kv := range vals { switch kv.Key { case jdh.KeyComment: v := "" if len(kv.Value) > 0 { v = strings.TrimSpace(kv.Value[0]) } if tax.Comment == v { continue } tax.Comment = v case jdh.KeyExtern: ok := false for _, v := range kv.Value { v := strings.TrimSpace(v) if len(v) == 0 { continue } serv, ext, err := jdh.ParseExtern(v) if err != nil { continue } if len(ext) == 0 { if !t.delExtern(tx, serv) { continue } ok = true continue } if t.addExtern(tx, v) != nil { continue } ok = true } if !ok { continue } case jdh.TaxAuthority: v := "" if len(kv.Value) > 0 { v = strings.Join(strings.Fields(kv.Value[0]), " ") } if tax.Authority == v { continue } tax.Authority = v case jdh.TaxName: nm := "" if len(kv.Value) > 0 { nm = strings.Join(strings.Fields(kv.Value[0]), " ") } if len(nm) == 0 { return fmt.Errorf("new name for %s undefined", tx.data.Name) } if tax.Name == nm { continue } // remove the old name nmLow := strings.ToLower(tax.Name) v := t.names.Lookup(nmLow).([]*taxon) v = delTaxFromList(v, tx) if len(v) > 0 { t.names.Set(nmLow, v) } else { t.names.Delete(nmLow) } tax.Name = nm // add the new name nmLow = strings.ToLower(tax.Name) vi := t.names.Lookup(nmLow) var txLs []*taxon if vi == nil { txLs = []*taxon{tx} } else { txLs = append(vi.([]*taxon), tx) } t.names.Set(nmLow, txLs) case jdh.TaxParent: p := t.root if len(kv.Value) > 0 { v := strings.TrimSpace(kv.Value[0]) if len(v) == 0 { continue } var ok bool p, ok = t.ids[v] if !ok { return fmt.Errorf("new parent [%s] for taxon %s not in database", kv.Value, tax.Name) } } if tx.parent == p { continue } if (!tax.IsValid) && (p == t.root) { return fmt.Errorf("taxon %s is a synonym, it requires a parent", tax.Name) } if (p != t.root) && (!p.data.IsValid) { return fmt.Errorf("new parent [%s] for taxon %s is a synonym", p.data.Name, tax.Name) } if !p.isDescValid(tax.Rank, tax.IsValid) { return fmt.Errorf("taxon %s rank incompatible with new parent [%s] hierarchy", tax.Name, p.data.Name) } tx.parent.childs = delTaxFromList(tx.parent.childs, tx) p.childs = append(p.childs, tx) tx.parent = p if p == t.root { tax.Parent = "" } else { tax.Parent = p.data.Id } case jdh.TaxRank: v := jdh.Unranked if len(kv.Value) > 0 { v = jdh.GetRank(strings.TrimSpace(kv.Value[0])) } if tax.Rank == v { continue } if !tx.parent.isDescValid(v, tax.IsValid) { return fmt.Errorf("new rank [%s] for taxon %s incompatible with taxonomy hierarchy", v, tax.Name) } tax.Rank = v case jdh.TaxSynonym: p := tx.parent if len(kv.Value) > 0 { v := strings.TrimSpace(kv.Value[0]) if len(v) == 0 { continue } var ok bool p, ok = t.ids[v] if !ok { return fmt.Errorf("new parent [%s] for taxon %s not in database", kv.Value, tax.Name) } } if (p == tx.parent) && (!tax.IsValid) { continue } if p == t.root { return fmt.Errorf("taxon %s can not be a synonym: no new parent defined", tax.Name) } if p != tx.parent { if !p.isDescValid(tax.Rank, false) { return fmt.Errorf("taxon %s rank incompatible with new parent [%s] hierarchy", tax.Name, p.data.Name) } tx.parent.childs = delTaxFromList(tx.parent.childs, tx) tx.parent = p tax.Parent = p.data.Id p.childs = append(p.childs, tx) } for _, d := range tx.childs { d.parent = p d.data.Parent = p.data.Id p.childs = append(p.childs, d) } tx.childs = nil tax.IsValid = false case jdh.TaxValid: if tax.IsValid { continue } tx.parent.childs = delTaxFromList(tx.parent.childs, tx) tx.parent = tx.parent.parent tx.data.Parent = tx.parent.data.Id tx.parent.childs = append(tx.parent.childs, tx) tax.IsValid = true default: continue } t.changed = true } return nil }
// List returns a list of taxons. func (t *taxonomy) list(vals []jdh.KeyValue) (*list.List, error) { l := list.New() nameList := false noVal := true // creates the list for _, kv := range vals { switch kv.Key { case jdh.TaxChildren: tx := t.root if len(kv.Value) > 0 { v := strings.TrimSpace(kv.Value[0]) if len(v) > 0 { var ok bool if tx, ok = t.ids[v]; !ok { return nil, fmt.Errorf("taxon %s not in database", kv.Value) } } } for _, d := range tx.childs { if d.data.IsValid { l.PushBack(d.data) } } noVal = false case jdh.TaxSynonyms: if len(kv.Value) == 0 { return l, nil } tx := t.root v := strings.TrimSpace(kv.Value[0]) if len(v) == 0 { return l, nil } var ok bool if tx, ok = t.ids[v]; !ok { return nil, fmt.Errorf("taxon %s not in database", kv.Value) } for _, d := range tx.childs { if !d.data.IsValid { l.PushBack(d.data) } } noVal = false case jdh.TaxParents: if len(kv.Value) == 0 { return l, nil } v := strings.TrimSpace(kv.Value[0]) if len(v) == 0 { return l, nil } tx, ok := t.ids[v] if !ok { return nil, fmt.Errorf("taxon %s not in database", kv.Value) } for p := tx.parent; p != t.root; p = p.parent { l.PushBack(p.data) } noVal = false case jdh.TaxName: if len(kv.Value) == 0 { return nil, errors.New("taxon without identification") } nm := strings.ToLower(strings.Join(strings.Fields(kv.Value[0]), " ")) if len(nm) == 0 { return nil, errors.New("taxon without identification") } i := strings.Index(nm, "*") if i == 0 { return nil, errors.New("taxon without identification") } var ls *list.List if i > 0 { prefix := nm[:i] ls = t.names.Prefix(prefix) } else { ls = list.New() if v := t.names.Lookup(nm); v != nil { ls.PushBack(v) } } for e := ls.Front(); e != nil; e = e.Next() { tl := e.Value.([]*taxon) for _, tx := range tl { l.PushBack(tx.data) } } noVal = false nameList = true } if !noVal { break } } if noVal { return nil, errors.New("taxon without identification") } if !nameList { return l, nil } // filters of the list. for _, kv := range vals { if l.Len() == 0 { break } if len(kv.Value) == 0 { continue } switch kv.Key { case jdh.TaxParent: pId := strings.TrimSpace(kv.Value[0]) if len(pId) == 0 { continue } for e := l.Front(); e != nil; { nx := e.Next() tax := e.Value.(*jdh.Taxon) if !t.isDesc(tax.Id, pId) { l.Remove(e) } e = nx } case jdh.TaxParentName: p := strings.Join(strings.Fields(kv.Value[0]), " ") if len(p) == 0 { continue } for e := l.Front(); e != nil; { nx := e.Next() tax := e.Value.(*jdh.Taxon) if !t.hasParentName(tax.Id, p) { l.Remove(e) } e = nx } case jdh.TaxRank: rank := jdh.GetRank(strings.TrimSpace(kv.Value[0])) for e := l.Front(); e != nil; { nx := e.Next() tax := e.Value.(*jdh.Taxon) if tax.Rank != rank { l.Remove(e) } e = nx } } } return l, nil }