domogeek/vendor/github.com/dolanor/caldav-go/icalendar/unmarshal.go

366 lines
12 KiB
Go
Raw Normal View History

2022-04-18 10:47:47 +00:00
package icalendar
import (
"fmt"
"github.com/dolanor/caldav-go/icalendar/properties"
"github.com/dolanor/caldav-go/utils"
"log"
"reflect"
"regexp"
"strconv"
"strings"
)
var _ = log.Print
var splitter = regexp.MustCompile("\r?\n")
type token struct {
name string
components map[string][]*token
properties map[properties.PropertyName][]*properties.Property
}
func tokenize(encoded string) (*token, error) {
if encoded = strings.TrimSpace(encoded); encoded == "" {
return nil, utils.NewError(tokenize, "no content to tokenize", encoded, nil)
}
return tokenizeSlice(splitter.Split(encoded, -1))
}
func tokenizeSlice(slice []string, name ...string) (*token, error) {
tok := new(token)
size := len(slice)
if len(name) > 0 {
tok.name = name[0]
} else if size <= 0 {
return nil, utils.NewError(tokenizeSlice, "token has no content", slice, nil)
}
tok.properties = make(map[properties.PropertyName][]*properties.Property, 0)
tok.components = make(map[string][]*token, 0)
for i := 0; i < size; i++ {
// Handle iCalendar's space-indented line break format
// See: https://www.ietf.org/rfc/rfc2445.txt section 4.1
// "a long line can be split between any two characters by inserting a CRLF immediately followed by a single
// linear white space character"
line := slice[i]
for ; i < size-1 && strings.HasPrefix(slice[i+1], " "); i++ {
next := slice[i+1]
line += next[1:len(next)]
}
prop := properties.UnmarshalProperty(line)
if prop.Name.Equals("begin") {
for j := i; j < size; j++ {
end := strings.Replace(line, "BEGIN", "END", 1)
if slice[j] == end {
if component, err := tokenizeSlice(slice[i+1:j], prop.Value); err != nil {
msg := fmt.Sprintf("unable to tokenize %s component", prop.Value)
return nil, utils.NewError(tokenizeSlice, msg, slice, err)
} else {
existing, _ := tok.components[prop.Value]
tok.components[prop.Value] = append(existing, component)
i = j
break
}
}
}
} else if existing, ok := tok.properties[prop.Name]; ok {
tok.properties[prop.Name] = []*properties.Property{prop}
} else {
tok.properties[prop.Name] = append(existing, prop)
}
}
return tok, nil
}
func hydrateInterface(v reflect.Value, prop *properties.Property) (bool, error) {
// unable to decode into empty values
if isInvalidOrEmptyValue(v) {
return false, nil
}
var i = v.Interface()
var hasValue = false
// decode a value if possible
if decoder, ok := i.(properties.CanDecodeValue); ok {
if err := decoder.DecodeICalValue(prop.Value); err != nil {
return false, utils.NewError(hydrateInterface, "error decoding property value", v, err)
} else {
hasValue = true
}
}
// decode any params, if supported
if len(prop.Params) > 0 {
if decoder, ok := i.(properties.CanDecodeParams); ok {
if err := decoder.DecodeICalParams(prop.Params); err != nil {
return false, utils.NewError(hydrateInterface, "error decoding property parameters", v, err)
}
}
}
// finish with any validation
if validator, ok := i.(properties.CanValidateValue); ok {
if err := validator.ValidateICalValue(); err != nil {
return false, utils.NewError(hydrateInterface, "error validating property value", v, err)
}
}
return hasValue, nil
}
func hydrateLiteral(v reflect.Value, prop *properties.Property) (reflect.Value, error) {
literal := dereferencePointerValue(v)
switch literal.Kind() {
case reflect.Bool:
if i, err := strconv.ParseBool(prop.Value); err != nil {
return literal, utils.NewError(hydrateLiteral, "unable to decode bool "+prop.Value, literal.Interface(), err)
} else {
literal.SetBool(i)
}
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
if i, err := strconv.ParseInt(prop.Value, 10, 64); err != nil {
return literal, utils.NewError(hydrateLiteral, "unable to decode int "+prop.Value, literal.Interface(), err)
} else {
literal.SetInt(i)
}
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
if i, err := strconv.ParseUint(prop.Value, 10, 64); err != nil {
return literal, utils.NewError(hydrateLiteral, "unable to decode uint "+prop.Value, literal.Interface(), err)
} else {
literal.SetUint(i)
}
case reflect.Float32, reflect.Float64:
if i, err := strconv.ParseFloat(prop.Value, 64); err != nil {
return literal, utils.NewError(hydrateLiteral, "unable to decode float "+prop.Value, literal.Interface(), err)
} else {
literal.SetFloat(i)
}
case reflect.String:
literal.SetString(prop.Value)
default:
return literal, utils.NewError(hydrateLiteral, "unable to decode value as literal "+prop.Value, literal.Interface(), nil)
}
return literal, nil
}
func hydrateProperty(v reflect.Value, prop *properties.Property) error {
// check to see if the interface handles it's own hydration
if handled, err := hydrateInterface(v, prop); err != nil {
return utils.NewError(hydrateProperty, "unable to hydrate interface", v, err)
} else if handled {
return nil // exit early if handled by the interface
}
// if we got here, we need to create a new instance to
// set into the property.
var vnew, varr = newValue(v)
var vlit bool
// check to see if the new value handles it's own hydration
if handled, err := hydrateInterface(vnew, prop); err != nil {
return utils.NewError(hydrateProperty, "unable to hydrate new interface value", vnew, err)
} else if vlit = !handled; vlit {
// if not, treat it as a literal
if vnewlit, err := hydrateLiteral(vnew, prop); err != nil {
return utils.NewError(hydrateProperty, "unable to hydrate new literal value", vnew, err)
} else if _, err := hydrateInterface(vnewlit, prop); err != nil {
return utils.NewError(hydrateProperty, "unable to hydrate new literal interface value", vnewlit, err)
}
}
// now we can set the value
vnewval := dereferencePointerValue(vnew)
voldval := dereferencePointerValue(v)
// make sure we can set the new value into the provided pointer
if varr {
// for arrays, append the new value into the array structure
if !voldval.CanSet() {
return utils.NewError(hydrateProperty, "unable to set array value", v, nil)
} else {
voldval.Set(reflect.Append(voldval, vnewval))
}
} else if vlit {
// for literals, set the dereferenced value
if !voldval.CanSet() {
return utils.NewError(hydrateProperty, "unable to set literal value", v, nil)
} else {
voldval.Set(vnewval)
}
} else if !v.CanSet() {
return utils.NewError(hydrateProperty, "unable to set pointer value", v, nil)
} else {
// everything else should be a pointer, set it directly
v.Set(vnew)
}
return nil
}
func hydrateNestedComponent(v reflect.Value, component *token) error {
// create a new object to hold the property value
var vnew, varr = newValue(v)
if err := hydrateComponent(vnew, component); err != nil {
return utils.NewError(hydrateNestedComponent, "unable to decode component", component, err)
}
if varr {
// for arrays, append the new value into the array structure
voldval := dereferencePointerValue(v)
if !voldval.CanSet() {
return utils.NewError(hydrateNestedComponent, "unable to set array value", v, nil)
} else {
voldval.Set(reflect.Append(voldval, vnew))
}
} else if !v.CanSet() {
return utils.NewError(hydrateNestedComponent, "unable to set pointer value", v, nil)
} else {
// everything else should be a pointer, set it directly
v.Set(vnew)
}
return nil
}
func hydrateProperties(v reflect.Value, component *token) error {
vdref := dereferencePointerValue(v)
vtype := vdref.Type()
vkind := vdref.Kind()
if vkind != reflect.Struct {
return utils.NewError(hydrateProperties, "unable to hydrate properties of non-struct", v, nil)
}
n := vtype.NumField()
for i := 0; i < n; i++ {
prop := properties.PropertyFromStructField(vtype.Field(i))
if prop == nil {
continue // skip if field is ignored
}
vfield := vdref.Field(i)
// first try to hydrate property values
if properties, ok := component.properties[prop.Name]; ok {
for _, prop := range properties {
if err := hydrateProperty(vfield, prop); err != nil {
msg := fmt.Sprintf("unable to hydrate property %s", prop.Name)
return utils.NewError(hydrateProperties, msg, v, err)
}
}
}
// then try to hydrate components
vtemp, _ := newValue(vfield)
if tag, err := extractTagFromValue(vtemp); err != nil {
msg := fmt.Sprintf("unable to extract tag from property %s", prop.Name)
return utils.NewError(hydrateProperties, msg, v, err)
} else if components, ok := component.components[tag]; ok {
for _, comp := range components {
if err := hydrateNestedComponent(vfield, comp); err != nil {
msg := fmt.Sprintf("unable to hydrate component %s", prop.Name)
return utils.NewError(hydrateProperties, msg, v, err)
}
}
}
}
return nil
}
func hydrateComponent(v reflect.Value, component *token) error {
if tag, err := extractTagFromValue(v); err != nil {
return utils.NewError(hydrateComponent, "error extracting tag from value", component, err)
} else if tag != component.name {
msg := fmt.Sprintf("expected %s and found %s", tag, component.name)
return utils.NewError(hydrateComponent, msg, component, nil)
} else if err := hydrateProperties(v, component); err != nil {
return utils.NewError(hydrateComponent, "unable to hydrate properties", component, err)
}
return nil
}
func hydrateComponents(v reflect.Value, components []*token) error {
vdref := dereferencePointerValue(v)
for i, component := range components {
velem := reflect.New(vdref.Type().Elem())
if err := hydrateComponent(velem, component); err != nil {
msg := fmt.Sprintf("unable to hydrate component %d", i)
return utils.NewError(hydrateComponent, msg, component, err)
} else {
v.Set(reflect.Append(vdref, velem))
}
}
return nil
}
func hydrateValue(v reflect.Value, component *token) error {
if !v.IsValid() || v.Kind() != reflect.Ptr {
return utils.NewError(hydrateValue, "unmarshal target must be a valid pointer", v, nil)
}
// handle any encodable properties
if encoder, isprop := v.Interface().(properties.CanEncodeName); isprop {
if name, err := encoder.EncodeICalName(); err != nil {
return utils.NewError(hydrateValue, "unable to lookup property name", v, err)
} else if properties, found := component.properties[name]; !found || len(properties) == 0 {
return utils.NewError(hydrateValue, "no matching propery values found for "+string(name), v, nil)
} else if len(properties) > 1 {
return utils.NewError(hydrateValue, "more than one property value matches single property interface", v, nil)
} else {
return hydrateProperty(v, properties[0])
}
}
// handle components
vkind := dereferencePointerValue(v).Kind()
if tag, err := extractTagFromValue(v); err != nil {
return utils.NewError(hydrateValue, "unable to extract component tag", v, err)
} else if components, found := component.components[tag]; !found || len(components) == 0 {
msg := fmt.Sprintf("unable to find matching component for %s", tag)
return utils.NewError(hydrateValue, msg, v, nil)
} else if vkind == reflect.Array || vkind == reflect.Slice {
return hydrateComponents(v, components)
} else if len(components) > 1 {
return utils.NewError(hydrateValue, "non-array interface provided but more than one component found!", v, nil)
} else {
return hydrateComponent(v, components[0])
}
}
// decodes encoded icalendar data into a native interface
func Unmarshal(encoded string, into interface{}) error {
if component, err := tokenize(encoded); err != nil {
return utils.NewError(Unmarshal, "unable to tokenize encoded data", encoded, err)
} else {
return hydrateValue(reflect.ValueOf(into), component)
}
}