@@ -866,51 +866,57 @@ func isZero(rv reflect.Value) bool {
866
866
return rv .Interface () == reflect .Zero (rv .Type ()).Interface () // TODO(dennwc): rewrite
867
867
}
868
868
869
- func (c * Config ) writeOneValReflect (w quad.Writer , id quad.Value , pred quad.Value , rv reflect.Value , rev bool ) error {
870
- if isZero (rv ) {
871
- return nil
872
- }
873
- targ , ok := quad .AsValue (rv .Interface ())
874
- if ! ok {
875
- if rv .Kind () == reflect .Ptr {
876
- rv = rv .Elem ()
877
- }
878
- targ , ok = quad .AsValue (rv .Interface ())
879
- if ! ok && rv .Kind () == reflect .Struct {
880
- sid , err := c .WriteAsQuads (w , rv .Interface ())
881
- if err != nil {
882
- return err
883
- }
884
- targ , ok = sid , true
885
- }
886
- }
887
- if ! ok {
888
- return fmt .Errorf ("unsupported type: %T" , rv .Interface ())
889
- }
890
- s , o := id , targ
869
+ func (c * Config ) writeQuad (w quad.Writer , s , p , o quad.Value , rev bool ) error {
891
870
if rev {
892
871
s , o = o , s
893
872
}
894
- return w .WriteQuad (quad.Quad {Subject : s , Predicate : pred , Object : o , Label : c .Label })
873
+ return w .WriteQuad (quad.Quad {Subject : s , Predicate : p , Object : o , Label : c .Label })
895
874
}
896
875
897
- func (c * Config ) writeValueAs (w quad.Writer , id quad.Value , rv reflect.Value , pref string , rules fieldRules ) error {
898
- if rv .Kind () == reflect .Ptr {
899
- rv = rv .Elem ()
876
+ // writeOneValReflect writes a set of quads corresponding to a value. It may omit writing quads if value is zero.
877
+ func (c * Config ) writeOneValReflect (w quad.Writer , id quad.Value , pred quad.Value , rv reflect.Value , rev bool , seen map [uintptr ]quad.Value ) error {
878
+ if isZero (rv ) {
879
+ return nil
900
880
}
901
- rt := rv .Type ()
881
+ // write field value and get an ID
882
+ sid , err := c .writeAsQuads (w , rv , seen )
883
+ if err != nil {
884
+ return err
885
+ }
886
+ // write a quad pointing to this value
887
+ return c .writeQuad (w , id , pred , sid , rev )
888
+ }
889
+
890
+ func (c * Config ) writeTypeInfo (w quad.Writer , id quad.Value , rt reflect.Type ) error {
902
891
typesMu .RLock ()
903
892
iri := typeToIRI [rt ]
904
893
typesMu .RUnlock ()
905
- if iri != quad .IRI ("" ) {
906
- if err := w .WriteQuad (quad.Quad {Subject : id , Predicate : c .iri (iriType ), Object : c .iri (iri ), Label : c .Label }); err != nil {
907
- return err
894
+ if iri == quad .IRI ("" ) {
895
+ return nil
896
+ }
897
+ return c .writeQuad (w , id , c .iri (iriType ), c .iri (iri ), false )
898
+ }
899
+
900
+ func (c * Config ) writeValueAs (w quad.Writer , id quad.Value , rv reflect.Value , pref string , rules fieldRules , seen map [uintptr ]quad.Value ) error {
901
+ switch kind := rv .Kind (); kind {
902
+ case reflect .Ptr , reflect .Map :
903
+ ptr := rv .Pointer ()
904
+ if _ , ok := seen [ptr ]; ok {
905
+ return nil
908
906
}
907
+ seen [ptr ] = id
908
+ if kind == reflect .Ptr {
909
+ rv = rv .Elem ()
910
+ }
911
+ }
912
+ rt := rv .Type ()
913
+ if err := c .writeTypeInfo (w , id , rt ); err != nil {
914
+ return err
909
915
}
910
916
for i := 0 ; i < rt .NumField (); i ++ {
911
917
f := rt .Field (i )
912
918
if f .Anonymous {
913
- if err := c .writeValueAs (w , id , rv .Field (i ), pref + f .Name + "." , rules ); err != nil {
919
+ if err := c .writeValueAs (w , id , rv .Field (i ), pref + f .Name + "." , rules , seen ); err != nil {
914
920
return err
915
921
}
916
922
continue
@@ -928,7 +934,7 @@ func (c *Config) writeValueAs(w quad.Writer, id quad.Value, rv reflect.Value, pr
928
934
if f .Type .Kind () == reflect .Slice {
929
935
sl := rv .Field (i )
930
936
for j := 0 ; j < sl .Len (); j ++ {
931
- if err := c .writeOneValReflect (w , id , r .Pred , sl .Index (j ), r .Rev ); err != nil {
937
+ if err := c .writeOneValReflect (w , id , r .Pred , sl .Index (j ), r .Rev , seen ); err != nil {
932
938
return err
933
939
}
934
940
}
@@ -937,7 +943,7 @@ func (c *Config) writeValueAs(w quad.Writer, id quad.Value, rv reflect.Value, pr
937
943
if ! r .Opt && isZero (fv ) {
938
944
return ErrReqFieldNotSet {Field : f .Name }
939
945
}
940
- if err := c .writeOneValReflect (w , id , r .Pred , fv , r .Rev ); err != nil {
946
+ if err := c .writeOneValReflect (w , id , r .Pred , fv , r .Rev , seen ); err != nil {
941
947
return err
942
948
}
943
949
}
@@ -991,29 +997,64 @@ func (c *Config) idFor(rules fieldRules, rt reflect.Type, rv reflect.Value, pref
991
997
//
992
998
// See LoadTo for a list of quads mapping rules.
993
999
func (c * Config ) WriteAsQuads (w quad.Writer , o interface {}) (quad.Value , error ) {
994
- if v , ok := o .(quad.Value ); ok {
995
- return v , nil
1000
+ return c .writeAsQuads (w , reflect .ValueOf (o ), make (map [uintptr ]quad.Value ))
1001
+ }
1002
+
1003
+ var reflQuadValue = reflect .TypeOf ((* quad .Value )(nil )).Elem ()
1004
+
1005
+ func (c * Config ) writeAsQuads (w quad.Writer , rv reflect.Value , seen map [uintptr ]quad.Value ) (quad.Value , error ) {
1006
+ rt := rv .Type ()
1007
+ // if node is a primitive - return directly
1008
+ if rt .Implements (reflQuadValue ) {
1009
+ return rv .Interface ().(quad.Value ), nil
1010
+ }
1011
+ prv := rv
1012
+ kind := rt .Kind ()
1013
+ // check if we've seen this node already
1014
+ switch kind {
1015
+ case reflect .Ptr , reflect .Map :
1016
+ ptr := prv .Pointer ()
1017
+ if sid , ok := seen [ptr ]; ok {
1018
+ return sid , nil
1019
+ }
1020
+ if kind == reflect .Ptr {
1021
+ rv = rv .Elem ()
1022
+ rt = rv .Type ()
1023
+ kind = rt .Kind ()
1024
+ }
996
1025
}
997
- rv := reflect .ValueOf (o )
998
- if rv .Kind () == reflect .Ptr {
999
- rv = rv .Elem ()
1026
+ // check if it's a type that quads package supports
1027
+ // note, that it may be a struct such as time.Time
1028
+ if val , ok := quad .AsValue (rv .Interface ()); ok {
1029
+ return val , nil
1000
1030
}
1001
- rt := rv .Type ()
1031
+ // TODO(dennwc): support maps
1032
+ if kind != reflect .Struct {
1033
+ return nil , fmt .Errorf ("unsupported type: %v" , rt )
1034
+ }
1035
+ // get conversion rules for this struct type
1002
1036
rules , err := c .rulesFor (rt )
1003
1037
if err != nil {
1004
1038
return nil , fmt .Errorf ("can't load rules: %v" , err )
1005
1039
}
1006
1040
if len (rules ) == 0 {
1007
1041
return nil , fmt .Errorf ("no rules for struct: %v" , rt )
1008
1042
}
1043
+ // get an ID from the struct value
1009
1044
id , err := c .idFor (rules , rt , rv , "" )
1010
1045
if err != nil {
1011
1046
return nil , err
1012
1047
}
1013
1048
if id == nil {
1014
- id = c .genID (o )
1049
+ id = c .genID (prv .Interface ())
1050
+ }
1051
+ // save a node ID to avoid loops
1052
+ switch prv .Kind () {
1053
+ case reflect .Ptr , reflect .Map :
1054
+ ptr := prv .Pointer ()
1055
+ seen [ptr ] = id
1015
1056
}
1016
- if err = c .writeValueAs (w , id , rv , "" , rules ); err != nil {
1057
+ if err = c .writeValueAs (w , id , rv , "" , rules , seen ); err != nil {
1017
1058
return nil , err
1018
1059
}
1019
1060
return id , nil
@@ -1031,13 +1072,14 @@ func (c *Config) WriteNamespaces(w quad.Writer, n *voc.Namespaces) error {
1031
1072
if err != nil {
1032
1073
return fmt .Errorf ("can't load rules: %v" , err )
1033
1074
}
1075
+ seen := make (map [uintptr ]quad.Value )
1034
1076
for _ , ns := range n .List () {
1035
1077
obj := namespace {
1036
1078
Full : quad .IRI (ns .Full ),
1037
1079
Prefix : quad .IRI (ns .Prefix ),
1038
1080
}
1039
1081
rv := reflect .ValueOf (obj )
1040
- if err = c .writeValueAs (w , obj .Full , rv , "" , rules ); err != nil {
1082
+ if err = c .writeValueAs (w , obj .Full , rv , "" , rules , seen ); err != nil {
1041
1083
return err
1042
1084
}
1043
1085
}
0 commit comments