package orm import ( "fmt" "math" "reflect" "strconv" "strings" "sync" ) type table struct { rt reflect.Type rv reflect.Value l *sync.RWMutex tFn, cFn MapperFn cols columns rels relations mapped bool } func (t *table) Make() MappableInterface { return reflect.New(t.getType()).Interface().(MappableInterface) } func (t *table) CallMethod(i MappableInterface, n string, args ...interface{}) (ret []interface{}, err error) { if t.getValue(true).MethodByName(n).IsValid() { fn := reflect.ValueOf(i).MethodByName(n) fnt := fn.Type() in := []reflect.Value{} if fnt.IsVariadic() && len(args) < (fnt.NumIn()-1) { return ret, fmt.Errorf("To few arguments to «%s». Got «%d», expected «%d»", n, len(args), fnt.NumIn()-1) } else if !fnt.IsVariadic() && len(args) != fnt.NumIn() { return ret, fmt.Errorf("To few arguments to «%s». Got «%d», expected «%d»", n, len(args), fnt.NumIn()-1) } for x := 0; x < len(args); x++ { var inType reflect.Type if fnt.IsVariadic() && x >= fnt.NumIn()-1 { inType = fnt.In(fnt.NumIn() - 1).Elem() } else { inType = fnt.In(x) } argv := reflect.ValueOf(args[x]) if !argv.IsValid() || !argv.Type().ConvertibleTo(inType) { switch inType.Kind() { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: var val uint64 = 0 argx := args[x].([]uint8) for xi := len(argx) - 1; xi >= 0; xi-- { val += uint64(math.Pow(10, float64(len(argx)-xi-1))) * uint64(argx[xi]-'0') } args[x] = val return t.CallMethod(i, n, args...) case reflect.Float32, reflect.Float64: if val, err := strconv.ParseFloat(string(args[x].([]byte)), 64); err == nil { args[x] = val return t.CallMethod(i, n, args...) } case reflect.Complex64, reflect.Complex128: // Not implemented return ret, fmt.Errorf("Complex not implemented") } return ret, fmt.Errorf("Invalid argument to «%s». Got %s, expected %s", n, argv.String(), inType.String()) } in = append(in, argv.Convert(inType)) } var out []reflect.Value defer func() { if r := recover(); r != nil { fmt.Println("Recovered: ", r) err = fmt.Errorf("Recovered: %v", r) } }() out = fn.Call(in)[0:fnt.NumOut()] if strings.HasPrefix(n, "Get") { for _, val := range out { switch val.Kind() { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: ret = append(ret, val.Uint()) case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: ret = append(ret, val.Int()) case reflect.Float32, reflect.Float64: ret = append(ret, val.Float()) case reflect.String: ret = append(ret, val.String()) case reflect.Interface: if !val.IsNil() && val.CanInterface() && val.Type().Implements(reflect.TypeOf((*error)(nil)).Elem()) { err = val.Interface().(error) } case reflect.Struct, reflect.Ptr: if val.CanInterface() && !val.IsNil() { ret = append(ret, val.Interface()) } else if val.IsNil() { ret = append(ret, nil) } } } } return ret, err } return ret, fmt.Errorf("Invalid method «%s» on «%s»", n, t.getType()) } // getPrimaryKey tries to find primary key func (t *table) getPrimaryKey() *column { var pkey *column = nil if c := t.hasTaggedColumn("primary"); c != nil { pkey = c } else if c := t.hasColumn("Id"); c != nil { pkey = c } return pkey } func (t *table) getRelations() map[relType][]relation { return t.rels.rmap } func (t *table) getColumns() columns { return t.cols } // hasTaggetColumn checks for a collumn tagget as func (t *table) hasTaggedColumn(ct string) *column { for _, col := range t.cols { if col.hasTag(ct) { return &col } } return nil } // hasColumn checks for a column by name func (t *table) hasColumn(c string) *column { for _, col := range t.cols { if col.ref == t.cFn(c) { return &col } } return nil } // addField from struct; both «relations» and «columns» // Communicates over a channel, to do relation mapping func (t *table) addField(f field, cbCh chan<- mapperCallback) { if f.hasTags("omit", "-") { return // Skip "omitted" and "dashed" fields } switch f.getType() { case Relation: // Make callback to itself, relations should be mapped after columns cbCh <- mapperCallback{ from: t.getStructName(), to: t.getStructName(), fn: func(self *table) { // Get Primary Key pkey := self.getPrimaryKey() // Or at least try to if pkey == nil { return } // Predict related table name rtbl := f.getFieldType() // And check for tag if tag, ok := f.getTag("table"); ok { rtbl = tag } // Predict column name cn := f.getFieldName() + "Id" // And check for tag if tag, ok := f.getTag("fkey"); ok { cn = tag } /* if self.getStructName() == "PlatformCalendar" { fmt.Println(self.getStructName(), f.getFieldType(), cn) } */ // Check if it contains reference itself if c := self.hasColumn(cn); c != nil { // Make a call to load related table into scope; // we need its addr and must be loaded from mapper cbCh <- mapperCallback{ from: self.getStructName(), to: rtbl, fn: func(tbl *table) { key := tbl.getPrimaryKey() switch f.MakeType().Kind() { case reflect.Map, reflect.Slice: self.rels.addRelation(belongsTo, relation{tbl, f, *c, *key}) default: self.rels.addRelation(belongsTo, relation{tbl, f, *key, *c}) } }, } } else { // Or predict column name in related table cn = self.getStructName() + "Id" // Check for reference tag if tag, ok := f.getTag("ref"); ok { cn = tag } // Make a callback to the related table to check for relationg cbCh <- mapperCallback{ from: self.getStructName(), to: rtbl, fn: func(tbl *table) { // Check for relation on column mane if c := tbl.hasColumn(cn); c != nil { has := hasOne switch f.MakeType().Kind() { case reflect.Map, reflect.Slice: has = hasMany } self.rels.addRelation(has, relation{tbl, f, *c, *pkey}) } }, } } return }, } default: // Add column switch f.getKind() { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: fallthrough // Support all Uint types case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: fallthrough // Support all Int types case reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128: fallthrough // Support all Float and Complex types case reflect.String, reflect.Bool, reflect.Struct: // Support string and boolean // Map column name dbf := t.cFn(f.getFieldName()) // override with tagging if tag, ok := f.getTag("field"); ok { dbf = t.cFn(tag) } t.cols = append(t.cols, column{ f, dbf, }) case reflect.Ptr: f.t = f.t.Elem() f.v = f.v.Elem() t.addField(f, cbCh) default: fmt.Println(t.getStructName(), "not supporting", f) } } } // getType returns the reflect.Type of the «table» func (t *table) getType() reflect.Type { return t.rt } // getValue returns the reflect.Value of the «table» func (t *table) getValue(ptr ...bool) reflect.Value { if len(ptr) > 0 && ptr[0] { return t.rv } return t.rv.Elem() } // isMapped returns true when columns is mapped // Not relations! They will be mapped in separate routines func (t *table) isMapped() bool { return t.mapped } // Lock read lock func (t *table) Lock() *table { t.l.RLock() return t } // Unlock read lock func (t *table) Unlock() { t.l.RUnlock() } // lock write lock func (t *table) lock() *table { t.l.Lock() return t } // unlock write lock func (t *table) unlock() { t.l.Unlock() } // getStructName returns the name of the struct func (t *table) getStructName() string { return t.getType().Name() } // getName returns the mapped table name // as identified in the database func (t *table) getName(q bool) string { if q { return SqlFlavor.Quote(t.getName(false)) } return t.tFn(t.getType().Name()) } func (t *table) getNameAs(q bool) string { return t.getName(q) + " AS " + t.getAlias(q) } func (t *table) getAlias(q bool) string { if q { return SqlFlavor.Quote(t.getAlias(false)) } return t.getStructName() } // tables is simply a collection of tables type tables map[string]*table