github.com/goki/ki@v1.1.11/ki/io.go (about)

     1  // Copyright (c) 2018, The GoKi Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package ki
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"encoding/json"
    11  	"encoding/xml"
    12  	"errors"
    13  	"fmt"
    14  	"io"
    15  	"io/ioutil"
    16  	"log"
    17  	"os"
    18  	"reflect"
    19  	"strconv"
    20  	"strings"
    21  
    22  	"github.com/goki/ki/kit"
    23  )
    24  
    25  // see https://github.com/goki/ki/wiki/Naming for IO naming conventions
    26  
    27  // TODO: switch to using Decode / Encode instead of
    28  // [Un]MarshalJSON which uses byte[] instead of io.Reader / Writer..
    29  
    30  // JSONTypePrefix is the first thing output in a ki tree JSON output file,
    31  // specifying the type of the root node of the ki tree -- this info appears
    32  // all on one { } bracketed line at the start of the file, and can also be
    33  // used to identify the file as a ki tree JSON file
    34  var JSONTypePrefix = []byte("{\"ki.RootType\": ")
    35  
    36  // JSONTypeSuffix is just the } and \n at the end of the prefix line
    37  var JSONTypeSuffix = []byte("}\n")
    38  
    39  // WriteJSON writes the tree to an io.Writer, using MarshalJSON -- also
    40  // saves a critical starting record that allows file to be loaded de-novo
    41  // and recreate the proper root type for the tree.
    42  // This calls UniquifyNamesAll because it is essential that names be unique
    43  // at this point.
    44  func (n *Node) WriteJSON(writer io.Writer, indent bool) error {
    45  	err := ThisCheck(n)
    46  	if err != nil {
    47  		return err
    48  	}
    49  	UniquifyNamesAll(n.This())
    50  	var b []byte
    51  	if indent {
    52  		b, err = json.MarshalIndent(n.This(), "", "  ")
    53  	} else {
    54  		b, err = json.Marshal(n.This())
    55  	}
    56  	if err != nil {
    57  		log.Println(err)
    58  		return err
    59  	}
    60  	knm := kit.Types.TypeName(Type(n.This()))
    61  	tstr := string(JSONTypePrefix) + fmt.Sprintf("\"%v\"}\n", knm)
    62  	nwb := make([]byte, len(b)+len(tstr))
    63  	copy(nwb, []byte(tstr))
    64  	copy(nwb[len(tstr):], b) // is there a way to avoid this?
    65  	_, err = writer.Write(nwb)
    66  	if err != nil {
    67  		log.Println(err)
    68  	}
    69  	return err
    70  }
    71  
    72  // SaveJSON saves the tree to a JSON-encoded file, using WriteJSON.
    73  func (n *Node) SaveJSON(filename string) error {
    74  	fp, err := os.Create(filename)
    75  	defer fp.Close()
    76  	if err != nil {
    77  		log.Println(err)
    78  		return err
    79  	}
    80  	bw := bufio.NewWriter(fp)
    81  	err = n.WriteJSON(bw, Indent) // use indent by default
    82  	if err != nil {
    83  		log.Println(err)
    84  		return err
    85  	}
    86  	err = bw.Flush()
    87  	if err != nil {
    88  		log.Println(err)
    89  	}
    90  	return err
    91  }
    92  
    93  // ReadJSON reads and unmarshals tree starting at this node, from a
    94  // JSON-encoded byte stream via io.Reader.  First element in the stream
    95  // must be of same type as this node -- see ReadNewJSON function to
    96  // construct a new tree.  Uses ConfigureChildren to minimize changes from
    97  // current tree relative to loading one -- wraps UnmarshalJSON and calls
    98  // UnmarshalPost to recover pointers from paths.
    99  func (n *Node) ReadJSON(reader io.Reader) error {
   100  	err := ThisCheck(n)
   101  	if err != nil {
   102  		log.Println(err)
   103  		return err
   104  	}
   105  	b, err := ioutil.ReadAll(reader)
   106  	if err != nil {
   107  		log.Println(err)
   108  		return err
   109  	}
   110  	updt := n.UpdateStart()
   111  	stidx := 0
   112  	if bytes.HasPrefix(b, JSONTypePrefix) { // skip type
   113  		stidx = bytes.Index(b, JSONTypeSuffix) + len(JSONTypeSuffix)
   114  	}
   115  	// todo: use json.NewDecoder, Decode instead -- need to deal with TypePrefix etc above
   116  	err = json.Unmarshal(b[stidx:], n.This()) // key use of this!
   117  	UnmarshalPost(n.This())
   118  	n.SetChildAdded() // this might not be set..
   119  	n.UpdateEnd(updt)
   120  	return err
   121  }
   122  
   123  // OpenJSON opens file over this tree from a JSON-encoded file -- see
   124  // ReadJSON for details, and OpenNewJSON for opening an entirely new tree.
   125  func (n *Node) OpenJSON(filename string) error {
   126  	fp, err := os.Open(filename)
   127  	defer fp.Close()
   128  	if err != nil {
   129  		log.Println(err)
   130  		return err
   131  	}
   132  	return n.ReadJSON(bufio.NewReader(fp))
   133  }
   134  
   135  // ReadNewJSON reads a new Ki tree from a JSON-encoded byte string, using type
   136  // information at start of file to create an object of the proper type
   137  func ReadNewJSON(reader io.Reader) (Ki, error) {
   138  	b, err := ioutil.ReadAll(reader)
   139  	if err != nil {
   140  		log.Println(err)
   141  		return nil, err
   142  	}
   143  	if bytes.HasPrefix(b, JSONTypePrefix) {
   144  		stidx := len(JSONTypePrefix) + 1
   145  		eidx := bytes.Index(b, JSONTypeSuffix)
   146  		bodyidx := eidx + len(JSONTypeSuffix)
   147  		tn := string(bytes.Trim(bytes.TrimSpace(b[stidx:eidx]), "\""))
   148  		typ := kit.Types.Type(tn)
   149  		if typ == nil {
   150  			return nil, fmt.Errorf("ki.OpenNewJSON: kit.Types type name not found: %v", tn)
   151  		}
   152  		root := NewOfType(typ)
   153  		InitNode(root)
   154  
   155  		updt := root.UpdateStart()
   156  		err = json.Unmarshal(b[bodyidx:], root)
   157  		UnmarshalPost(root)
   158  		root.SetChildAdded() // this might not be set..
   159  		root.UpdateEnd(updt)
   160  		return root, nil
   161  	}
   162  	return nil, fmt.Errorf("ki.OpenNewJSON -- type prefix not found at start of file -- must be there to identify type of root node of tree")
   163  }
   164  
   165  // OpenNewJSON opens a new Ki tree from a JSON-encoded file, using type
   166  // information at start of file to create an object of the proper type
   167  func OpenNewJSON(filename string) (Ki, error) {
   168  	fp, err := os.Open(filename)
   169  	defer fp.Close()
   170  	if err != nil {
   171  		log.Println(err)
   172  		return nil, err
   173  	}
   174  	return ReadNewJSON(bufio.NewReader(fp))
   175  }
   176  
   177  // WriteXML writes the tree to an XML-encoded byte string over io.Writer
   178  // using MarshalXML.
   179  func (n *Node) WriteXML(writer io.Writer, indent bool) error {
   180  	err := ThisCheck(n)
   181  	if err != nil {
   182  		log.Println(err)
   183  		return err
   184  	}
   185  	var b []byte
   186  	if indent {
   187  		b, err = xml.MarshalIndent(n.This(), "", "  ")
   188  	} else {
   189  		b, err = xml.Marshal(n.This())
   190  	}
   191  	if err != nil {
   192  		log.Println(err)
   193  		return err
   194  	}
   195  	_, err = writer.Write(b)
   196  	if err != nil {
   197  		log.Println(err)
   198  		return err
   199  	}
   200  	return nil
   201  }
   202  
   203  // ReadXML reads the tree from an XML-encoded byte string over io.Reader, calls
   204  // UnmarshalPost to recover pointers from paths.
   205  func (n *Node) ReadXML(reader io.Reader) error {
   206  	var err error
   207  	if err = ThisCheck(n); err != nil {
   208  		log.Println(err)
   209  		return err
   210  	}
   211  	b, err := ioutil.ReadAll(reader)
   212  	if err != nil {
   213  		log.Println(err)
   214  		return err
   215  	}
   216  	updt := n.UpdateStart()
   217  	err = xml.Unmarshal(b, n.This()) // key use of this!
   218  	UnmarshalPost(n.This())
   219  	n.SetChildAdded() // this might not be set..
   220  	n.UpdateEnd(updt)
   221  	return nil
   222  }
   223  
   224  // ParentAllChildren walks the tree down from current node and call
   225  // SetParent on all children -- needed after an Unmarshal.
   226  func ParentAllChildren(kn Ki) {
   227  	for _, child := range *kn.Children() {
   228  		if child != nil {
   229  			child.AsNode().Par = kn
   230  			ParentAllChildren(child)
   231  		}
   232  	}
   233  }
   234  
   235  // UnmarshalPost must be called after an Unmarshal -- calls
   236  // ParentAllChildren.
   237  func UnmarshalPost(kn Ki) {
   238  	ParentAllChildren(kn)
   239  }
   240  
   241  //////////////////////////////////////////////////////////////////////////
   242  // Slice
   243  
   244  // MarshalJSON saves the length and type, name information for each object in a
   245  // slice, as a separate struct-like record at the start, followed by the
   246  // structs for each element in the slice -- this allows the Unmarshal to first
   247  // create all the elements and then load them
   248  func (sl Slice) MarshalJSON() ([]byte, error) {
   249  	nk := len(sl)
   250  	b := make([]byte, 0, nk*100+20)
   251  	if nk == 0 {
   252  		b = append(b, []byte("null")...)
   253  		return b, nil
   254  	}
   255  	nstr := fmt.Sprintf("[{\"n\":%d,", nk)
   256  	b = append(b, []byte(nstr)...)
   257  	for i, kid := range sl {
   258  		// fmt.Printf("json out of %v\n", kid.Path())
   259  		knm := kit.Types.TypeName(reflect.TypeOf(kid).Elem())
   260  		tstr := fmt.Sprintf("\"type\":\"%v\", \"name\": \"%v\"", knm, kid.Name()) // todo: escape names!
   261  		b = append(b, []byte(tstr)...)
   262  		if i < nk-1 {
   263  			b = append(b, []byte(",")...)
   264  		}
   265  	}
   266  	b = append(b, []byte("},")...)
   267  	for i, kid := range sl {
   268  		var err error
   269  		var kb []byte
   270  		kb, err = json.Marshal(kid)
   271  		if err == nil {
   272  			b = append(b, []byte("{")...)
   273  			b = append(b, kb[1:len(kb)-1]...)
   274  			b = append(b, []byte("}")...)
   275  			if i < nk-1 {
   276  				b = append(b, []byte(",")...)
   277  			}
   278  		} else {
   279  			fmt.Printf("error doing json.Marshall from kid: %v\n", kid.Path())
   280  			log.Println(err)
   281  			fmt.Printf("output to point of error: %v\n", string(b))
   282  		}
   283  	}
   284  	b = append(b, []byte("]")...)
   285  	// fmt.Printf("json out: %v\n", string(b))
   286  	return b, nil
   287  }
   288  
   289  ///////////////////////////////////////////////////////////////////////////
   290  // JSON
   291  
   292  // UnmarshalJSON parses the length and type information for each object in the
   293  // slice, creates the new slice with those elements, and then loads based on
   294  // the remaining bytes which represent each element
   295  func (sl *Slice) UnmarshalJSON(b []byte) error {
   296  	// fmt.Printf("json in: %v\n", string(b))
   297  	if bytes.Equal(b, []byte("null")) {
   298  		*sl = nil
   299  		return nil
   300  	}
   301  	lb := bytes.IndexRune(b, '{')
   302  	rb := bytes.IndexRune(b, '}')
   303  	if lb < 0 || rb < 0 { // probably null
   304  		return nil
   305  	}
   306  	// todo: if name contains "," this won't work..
   307  	flds := bytes.Split(b[lb+1:rb], []byte(","))
   308  	if len(flds) == 0 {
   309  		return errors.New("Slice UnmarshalJSON: no child data found")
   310  	}
   311  	// fmt.Printf("flds[0]:\n%v\n", string(flds[0]))
   312  	ns := bytes.Index(flds[0], []byte("\"n\":"))
   313  	bn := bytes.TrimSpace(flds[0][ns+4:])
   314  
   315  	n64, err := strconv.ParseInt(string(bn), 10, 64)
   316  	if err != nil {
   317  		return err
   318  	}
   319  	n := int(n64)
   320  	if n == 0 {
   321  		return nil
   322  	}
   323  	// fmt.Printf("n parsed: %d from %v\n", n, string(bn))
   324  
   325  	tnl := make(kit.TypeAndNameList, n)
   326  
   327  	for i := 0; i < n; i++ {
   328  		fld := flds[2*i+1]
   329  		// fmt.Printf("fld:\n%v\n", string(fld))
   330  		ti := bytes.Index(fld, []byte("\"type\":"))
   331  		tn := string(bytes.Trim(bytes.TrimSpace(fld[ti+7:]), "\""))
   332  		fld = flds[2*i+2]
   333  		ni := bytes.Index(fld, []byte("\"name\":"))
   334  		nm := string(bytes.Trim(bytes.TrimSpace(fld[ni+7:]), "\""))
   335  		// fmt.Printf("making type: %v", tn)
   336  		typ := kit.Types.Type(tn)
   337  		if typ == nil {
   338  			return fmt.Errorf("ki.Slice UnmarshalJSON: kit.Types type name not found: %v", tn)
   339  		}
   340  		tnl[i].Type = typ
   341  		tnl[i].Name = nm
   342  	}
   343  
   344  	sl.Config(nil, tnl)
   345  
   346  	nwk := make([]Ki, n) // allocate new slice containing *pointers* to kids
   347  
   348  	for i, kid := range *sl {
   349  		nwk[i] = kid
   350  	}
   351  
   352  	cb := make([]byte, 0, 1+len(b)-rb)
   353  	cb = append(cb, []byte("[")...)
   354  	cb = append(cb, b[rb+2:]...)
   355  
   356  	// fmt.Printf("loading:\n%v", string(cb))
   357  
   358  	err = json.Unmarshal(cb, &nwk)
   359  	if err != nil {
   360  		return err
   361  	}
   362  	return nil
   363  }
   364  
   365  // todo: save N as an attr instead of a full element
   366  
   367  // MarshalXML saves the length and type information for each object in a
   368  // slice, as a separate struct-like record at the start, followed by the
   369  // structs for each element in the slice -- this allows the Unmarshal to first
   370  // create all the elements and then load them
   371  func (sl Slice) MarshalXML(e *xml.Encoder, start xml.StartElement) error {
   372  	tokens := []xml.Token{start}
   373  	nk := len(sl)
   374  	nt := xml.StartElement{Name: xml.Name{Space: "", Local: "N"}}
   375  	tokens = append(tokens, nt, xml.CharData(fmt.Sprintf("%d", nk)), xml.EndElement{Name: nt.Name})
   376  	for _, kid := range sl {
   377  		knm := kit.Types.TypeName(reflect.TypeOf(kid).Elem())
   378  		t := xml.StartElement{Name: xml.Name{Space: "", Local: "Type"}}
   379  		tokens = append(tokens, t, xml.CharData(knm), xml.EndElement{Name: t.Name})
   380  	}
   381  	for _, t := range tokens {
   382  		err := e.EncodeToken(t)
   383  		if err != nil {
   384  			return err
   385  		}
   386  	}
   387  	err := e.Flush()
   388  	if err != nil {
   389  		return err
   390  	}
   391  	for _, kid := range sl {
   392  		knm := reflect.TypeOf(kid).Elem().Name()
   393  		ct := xml.StartElement{Name: xml.Name{Space: "", Local: knm}}
   394  		err := e.EncodeElement(kid, ct)
   395  		if err != nil {
   396  			return err
   397  		}
   398  	}
   399  	err = e.EncodeToken(xml.EndElement{Name: start.Name})
   400  	if err != nil {
   401  		return err
   402  	}
   403  	err = e.Flush()
   404  	if err != nil {
   405  		return err
   406  	}
   407  	return nil
   408  }
   409  
   410  // DecodeXMLStartEl reads a start element token
   411  func DecodeXMLStartEl(d *xml.Decoder) (start xml.StartElement, err error) {
   412  	for {
   413  		var t xml.Token
   414  		t, err = d.Token()
   415  		if err != nil {
   416  			log.Printf("ki.DecodeXMLStartEl err %v\n", err)
   417  			return
   418  		}
   419  		switch tv := t.(type) {
   420  		case xml.StartElement:
   421  			start = tv
   422  			return
   423  		case xml.CharData: // actually passes the spaces and everything through here
   424  			continue
   425  		case xml.EndElement:
   426  			err = fmt.Errorf("ki.DecodeXMLStartEl: got unexpected EndElement")
   427  			log.Println(err)
   428  			return
   429  		default:
   430  			continue
   431  		}
   432  	}
   433  }
   434  
   435  // DecodeXMLEndEl reads an end element
   436  func DecodeXMLEndEl(d *xml.Decoder, start xml.StartElement) error {
   437  	for {
   438  		t, err := d.Token()
   439  		if err != nil {
   440  			log.Printf("ki.DecodeXMLEndEl err %v\n", err)
   441  			return err
   442  		}
   443  		switch tv := t.(type) {
   444  		case xml.EndElement:
   445  			if tv.Name != start.Name {
   446  				err = fmt.Errorf("ki.DecodeXMLEndEl: EndElement: %v does not match StartElement: %v", tv.Name, start.Name)
   447  				log.Println(err)
   448  				return err
   449  			}
   450  			return nil
   451  		case xml.CharData: // actually passes the spaces and everything through here
   452  			continue
   453  		case xml.StartElement:
   454  			err = fmt.Errorf("ki.DecodeXMLEndEl: got unexpected StartElement: %v", tv.Name)
   455  			log.Println(err)
   456  			return err
   457  		default:
   458  			continue
   459  		}
   460  	}
   461  }
   462  
   463  // DecodeXMLCharData reads char data..
   464  func DecodeXMLCharData(d *xml.Decoder) (val string, err error) {
   465  	for {
   466  		var t xml.Token
   467  		t, err = d.Token()
   468  		if err != nil {
   469  			log.Printf("ki.DecodeXMLCharData err %v\n", err)
   470  			return
   471  		}
   472  		switch tv := t.(type) {
   473  		case xml.CharData:
   474  			val = string([]byte(tv))
   475  			return
   476  		case xml.StartElement:
   477  			err = fmt.Errorf("ki.DecodeXMLCharData: got unexpected StartElement: %v", tv.Name)
   478  			log.Println(err)
   479  			return
   480  		case xml.EndElement:
   481  			err = fmt.Errorf("ki.DecodeXMLCharData: got unexpected EndElement: %v", tv.Name)
   482  			log.Println(err)
   483  			return
   484  		}
   485  	}
   486  }
   487  
   488  // DecodeXMLCharEl reads a start / chardata / end sequence of 3 elements, returning name, val
   489  func DecodeXMLCharEl(d *xml.Decoder) (name, val string, err error) {
   490  	var st xml.StartElement
   491  	st, err = DecodeXMLStartEl(d)
   492  	if err != nil {
   493  		return
   494  	}
   495  	name = st.Name.Local
   496  	val, err = DecodeXMLCharData(d)
   497  	if err != nil {
   498  		return
   499  	}
   500  	err = DecodeXMLEndEl(d, st)
   501  	if err != nil {
   502  		return
   503  	}
   504  	return
   505  }
   506  
   507  // UnmarshalXML parses the length and type information for each object in the
   508  // slice, creates the new slice with those elements, and then loads based on
   509  // the remaining bytes which represent each element
   510  func (sl *Slice) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
   511  	// for _, attr := range start.Attr {
   512  	// 	// todo: need to set the props from name / value -- don't have parent though!
   513  	// }
   514  	name, val, err := DecodeXMLCharEl(d)
   515  	if err != nil {
   516  		return err
   517  	}
   518  	if name == "N" {
   519  		n64, err := strconv.ParseInt(string(val), 10, 64)
   520  		if err != nil {
   521  			return err
   522  		}
   523  		n := int(n64)
   524  		if n == 0 {
   525  			return DecodeXMLEndEl(d, start)
   526  		}
   527  		// fmt.Printf("n parsed: %d from %v\n", n, string(val))
   528  		nwk := make([]Ki, 0, n) // allocate new slice
   529  
   530  		for i := 0; i < n; i++ {
   531  			name, val, err = DecodeXMLCharEl(d)
   532  			if name == "Type" {
   533  				tn := strings.TrimSpace(val)
   534  				// fmt.Printf("making type: %v\n", tn)
   535  				typ := kit.Types.Type(tn)
   536  				if typ == nil {
   537  					return fmt.Errorf("ki.Slice UnmarshalXML: kit.Types type name not found: %v", tn)
   538  				}
   539  				nkid := reflect.New(typ).Interface()
   540  				// fmt.Printf("nkid is new obj of type %T val: %+v\n", nkid, nkid)
   541  				kid, ok := nkid.(Ki)
   542  				if !ok {
   543  					return fmt.Errorf("ki.Slice UnmarshalXML: New child of type %v cannot convert to Ki", tn)
   544  				}
   545  				InitNode(kid)
   546  				nwk = append(nwk, kid)
   547  			}
   548  		}
   549  
   550  		for i := 0; i < n; i++ {
   551  			st, err := DecodeXMLStartEl(d)
   552  			if err != nil {
   553  				return err
   554  			}
   555  			// todo: could double-check st
   556  			err = d.DecodeElement(nwk[i], &st)
   557  			if err != nil {
   558  				log.Printf("%v", err)
   559  				return err
   560  			}
   561  		}
   562  		*sl = append(*sl, nwk...)
   563  		// } else {
   564  	}
   565  	// todo: in theory we could just parse a list of type names as tags, but for the "dump" format
   566  	// this is more robust.
   567  	return DecodeXMLEndEl(d, start) // final end
   568  }