github.com/boki/go-xmp@v1.0.1/xmp/array.go (about)

     1  // Copyright (c) 2017-2018 Alexander Eichhorn
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License"): you may
     4  // not use this file except in compliance with the License. You may obtain
     5  // a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    11  // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    12  // License for the specific language governing permissions and limitations
    13  // under the License.
    14  
    15  // Types as defined in ISO 16684-1:2011(E) 8.2.1 (Core value types)
    16  // - IntArray (ordered)
    17  // - StringList (ordered)
    18  // - StringArray (unordered)
    19  // - AltString (string, xml:lang support)
    20  
    21  package xmp
    22  
    23  import (
    24  	"encoding/xml"
    25  	"fmt"
    26  	"reflect"
    27  	"strconv"
    28  	"strings"
    29  )
    30  
    31  type Array interface {
    32  	Typ() ArrayType
    33  }
    34  
    35  // Choice / Alternative Arrays with xml:lang support
    36  //
    37  type ArrayType string
    38  
    39  const (
    40  	ArrayTypeOrdered     ArrayType = "Seq"
    41  	ArrayTypeUnordered   ArrayType = "Bag"
    42  	ArrayTypeAlternative ArrayType = "Alt"
    43  )
    44  
    45  // Note: when changing these tags, also update json.go for proper marshal/demarshal
    46  type AltItem struct {
    47  	Value     string `xmp:",chardata" json:"value"`
    48  	Lang      string `xmp:"xml:lang"  json:"lang"`
    49  	IsDefault bool   `xmp:"-"         json:"isDefault"`
    50  }
    51  
    52  func (x *AltItem) UnmarshalText(data []byte) error {
    53  	x.Value = string(data)
    54  	x.Lang = ""
    55  	x.IsDefault = true
    56  	return nil
    57  }
    58  
    59  func (x AltItem) GetLang() string {
    60  	if x.Lang == "" && x.IsDefault {
    61  		return "x-default"
    62  	}
    63  	return x.Lang
    64  }
    65  
    66  type AltString []AltItem
    67  
    68  func (x AltString) IsZero() bool {
    69  	return len(x) == 0
    70  }
    71  
    72  func NewAltString(items ...interface{}) AltString {
    73  	if len(items) == 0 {
    74  		return nil
    75  	}
    76  	a := make(AltString, 0)
    77  	for _, v := range items {
    78  		switch val := v.(type) {
    79  		case AltItem:
    80  			if val.Value != "" {
    81  				a = append(a, val)
    82  			}
    83  		case string:
    84  			if val != "" {
    85  				a = append(a, AltItem{Value: val})
    86  			}
    87  		case fmt.Stringer:
    88  			if s := val.String(); s != "" {
    89  				a = append(a, AltItem{Value: s})
    90  			}
    91  		}
    92  	}
    93  	a.EnsureDefault()
    94  	return a
    95  }
    96  
    97  // make sure there is exactly one a default and it's the first item
    98  func (a *AltString) EnsureDefault() {
    99  	l := len(*a)
   100  	if l > 1 {
   101  		idx := -1
   102  		for i := 0; i < l; i++ {
   103  			if (*a)[i].IsDefault {
   104  				if idx > -1 {
   105  					(*a)[i].IsDefault = false
   106  				} else {
   107  					idx = i
   108  				}
   109  			}
   110  		}
   111  		if idx != 0 {
   112  			(*a)[idx], (*a)[0] = (*a)[0], (*a)[idx]
   113  		}
   114  	} else if l == 1 {
   115  		(*a)[0].IsDefault = true
   116  	}
   117  }
   118  
   119  func (a AltString) Default() string {
   120  	for _, v := range a {
   121  		if v.IsDefault {
   122  			return v.Value
   123  		}
   124  	}
   125  	return ""
   126  }
   127  
   128  func (a AltString) Index(lang string) int {
   129  	for i, v := range a {
   130  		if v.Lang == lang {
   131  			return i
   132  		}
   133  	}
   134  	return -1
   135  }
   136  
   137  func (a AltString) Get(lang string) string {
   138  	if lang == "" {
   139  		return a.Default()
   140  	}
   141  	for _, v := range a {
   142  		if v.Lang == lang {
   143  			return v.Value
   144  		}
   145  	}
   146  	return ""
   147  }
   148  
   149  func (a *AltString) AddDefault(lang string, value string) {
   150  	if value == "" {
   151  		return
   152  	}
   153  	i := AltItem{
   154  		Value:     value,
   155  		Lang:      lang,
   156  		IsDefault: true,
   157  	}
   158  
   159  	// clear any previous default
   160  	for i, l := 0, len(*a); i < l; i++ {
   161  		(*a)[i].IsDefault = false
   162  	}
   163  
   164  	// add new default as first element
   165  	*a = append(AltString{i}, (*a)...)
   166  }
   167  
   168  func (a *AltString) AddUnique(lang string, value string) bool {
   169  	if value == "" {
   170  		return false
   171  	}
   172  	if idx := a.Index(lang); idx > -1 && (*a)[idx].Value == value {
   173  		return false
   174  	}
   175  	a.Add(lang, value)
   176  	return true
   177  }
   178  
   179  func (a *AltString) Add(lang string, value string) {
   180  	if value == "" {
   181  		return
   182  	}
   183  	*a = append(*a, AltItem{
   184  		Value:     value,
   185  		Lang:      lang,
   186  		IsDefault: false,
   187  	})
   188  	a.EnsureDefault()
   189  }
   190  
   191  func (a *AltString) Set(lang string, value string) {
   192  	if value == "" {
   193  		return
   194  	}
   195  	if i := a.Index(lang); i > -1 {
   196  		(*a)[i].Value = value
   197  	} else {
   198  		a.Add(lang, value)
   199  	}
   200  }
   201  
   202  func (a *AltString) RemoveLang(lang string) {
   203  	idx := -1
   204  	for i, v := range *a {
   205  		if v.Lang == lang {
   206  			idx = i
   207  			break
   208  		}
   209  	}
   210  	if idx > -1 {
   211  		*a = append((*a)[:idx], (*a)[idx+1:]...)
   212  		a.EnsureDefault()
   213  	}
   214  }
   215  
   216  func (a AltString) Typ() ArrayType {
   217  	return ArrayTypeAlternative
   218  }
   219  
   220  func (x AltString) MarshalXMP(e *Encoder, node *Node, m Model) error {
   221  	return MarshalArray(e, node, x.Typ(), x)
   222  }
   223  
   224  func (x *AltString) UnmarshalXMP(d *Decoder, node *Node, m Model) error {
   225  	if err := UnmarshalArray(d, node, x.Typ(), x); err != nil {
   226  		return err
   227  	}
   228  
   229  	// merge default with matching language, if any
   230  	var dup int = -1
   231  	for i, v := range *x {
   232  		if v.IsDefault {
   233  			for j, vv := range *x {
   234  				if i != j && v.Value == vv.Value {
   235  					(*x)[i].Lang = vv.Lang
   236  					dup = j
   237  					break
   238  				}
   239  			}
   240  			break
   241  		}
   242  	}
   243  
   244  	// remove duplicate
   245  	if dup > -1 {
   246  		*x = append((*x)[:dup], (*x)[dup+1:]...)
   247  	}
   248  
   249  	return nil
   250  }
   251  
   252  type AltStringArray []*AltString
   253  
   254  func (x AltStringArray) IsZero() bool {
   255  	return len(x) == 0
   256  }
   257  
   258  func (x *AltStringArray) Add(v *AltString) error {
   259  	if v == nil {
   260  		return nil
   261  	}
   262  	*x = append(*x, v)
   263  	return nil
   264  }
   265  
   266  func (x AltStringArray) Typ() ArrayType {
   267  	return ArrayTypeUnordered
   268  }
   269  
   270  func NewAltStringArray(items ...*AltString) AltStringArray {
   271  	x := make(AltStringArray, 0, len(items))
   272  	return append(x, items...)
   273  }
   274  
   275  func (x AltStringArray) MarshalXMP(e *Encoder, node *Node, m Model) error {
   276  	return MarshalArray(e, node, x.Typ(), x)
   277  }
   278  
   279  func (x *AltStringArray) UnmarshalXMP(d *Decoder, node *Node, m Model) error {
   280  	return UnmarshalArray(d, node, x.Typ(), x)
   281  }
   282  
   283  // Unordered Integer Arrays
   284  //
   285  type IntArray []int
   286  
   287  func (x IntArray) IsZero() bool {
   288  	return len(x) == 0
   289  }
   290  
   291  func (x *IntArray) Add(v int) error {
   292  	*x = append(*x, v)
   293  	return nil
   294  }
   295  
   296  func (x IntArray) Typ() ArrayType {
   297  	return ArrayTypeUnordered
   298  }
   299  
   300  func NewIntArray(items ...int) IntArray {
   301  	x := make(IntArray, 0, len(items))
   302  	return append(x, items...)
   303  }
   304  
   305  func (x IntArray) MarshalXMP(e *Encoder, node *Node, m Model) error {
   306  	return MarshalArray(e, node, x.Typ(), x)
   307  }
   308  
   309  func (x *IntArray) UnmarshalXMP(d *Decoder, node *Node, m Model) error {
   310  	return UnmarshalArray(d, node, x.Typ(), x)
   311  }
   312  
   313  // Ordered Integer Arrays
   314  //
   315  type IntList []int
   316  
   317  func (x IntList) IsZero() bool {
   318  	return len(x) == 0
   319  }
   320  
   321  func (x *IntList) Add(v int) error {
   322  	*x = append(*x, v)
   323  	return nil
   324  }
   325  
   326  func (x IntList) Typ() ArrayType {
   327  	return ArrayTypeOrdered
   328  }
   329  
   330  func NewIntList(items ...int) IntList {
   331  	x := make(IntList, 0, len(items))
   332  	return append(x, items...)
   333  }
   334  
   335  func (x IntList) String() string {
   336  	s := make([]string, len(x))
   337  	for i, v := range x {
   338  		s[i] = strconv.Itoa(v)
   339  	}
   340  	return strings.Join(s, " ")
   341  }
   342  
   343  func (x IntList) MarshalXMP(e *Encoder, node *Node, m Model) error {
   344  	return MarshalArray(e, node, x.Typ(), x)
   345  }
   346  
   347  func (x *IntList) UnmarshalXMP(d *Decoder, node *Node, m Model) error {
   348  	return UnmarshalArray(d, node, x.Typ(), x)
   349  }
   350  
   351  // Ordered String Arrays
   352  //
   353  type StringList []string
   354  
   355  func (x StringList) IsZero() bool {
   356  	return len(x) == 0
   357  }
   358  
   359  func (x *StringList) Add(v string) error {
   360  	if v == "" {
   361  		return nil
   362  	}
   363  	*x = append(*x, v)
   364  	return nil
   365  }
   366  
   367  func (x *StringList) AddUnique(v string) error {
   368  	if v == "" {
   369  		return nil
   370  	}
   371  	if !x.Contains(v) {
   372  		return x.Add(v)
   373  	}
   374  	return nil
   375  }
   376  
   377  func (x *StringList) Index(val string) int {
   378  	if val == "" {
   379  		return -1
   380  	}
   381  	for i, v := range *x {
   382  		if v == val {
   383  			return i
   384  		}
   385  	}
   386  	return -1
   387  }
   388  
   389  func (x *StringList) Contains(v string) bool {
   390  	return x.Index(v) > -1
   391  }
   392  
   393  func (x *StringList) Remove(v string) {
   394  	if v == "" {
   395  		return
   396  	}
   397  	if idx := x.Index(v); idx > -1 {
   398  		*x = append((*x)[:idx], (*x)[:idx+1]...)
   399  	}
   400  }
   401  
   402  func (x StringList) Typ() ArrayType {
   403  	return ArrayTypeOrdered
   404  }
   405  
   406  func NewStringList(items ...string) StringList {
   407  	if len(items) == 0 {
   408  		return nil
   409  	}
   410  	x := make(StringList, 0, len(items))
   411  	for _, v := range items {
   412  		if v == "" {
   413  			continue
   414  		}
   415  		x = append(x, v)
   416  	}
   417  	return x
   418  }
   419  
   420  func (x StringList) MarshalXMP(e *Encoder, node *Node, m Model) error {
   421  	return MarshalArray(e, node, x.Typ(), x)
   422  }
   423  
   424  func (x *StringList) UnmarshalXMP(d *Decoder, node *Node, m Model) error {
   425  	return UnmarshalArray(d, node, x.Typ(), x)
   426  }
   427  
   428  func (x *StringList) UnmarshalText(data []byte) error {
   429  	list := strings.Split(strings.Replace(string(data), "\r\n", "\n", -1), "\n")
   430  	*x = append(*x, list...)
   431  	return nil
   432  }
   433  
   434  func (x StringList) MarshalText() ([]byte, error) {
   435  	if len(x) == 0 {
   436  		return nil, nil
   437  	}
   438  	return []byte(strings.Join(x, "\n")), nil
   439  }
   440  
   441  // Unordered String Arrays
   442  //
   443  type StringArray []string
   444  
   445  func NewStringArray(items ...string) StringArray {
   446  	if len(items) == 0 {
   447  		return nil
   448  	}
   449  	x := make(StringArray, 0, len(items))
   450  	for _, v := range items {
   451  		if v == "" {
   452  			continue
   453  		}
   454  		x = append(x, v)
   455  	}
   456  	return x
   457  }
   458  
   459  func (x StringArray) IsZero() bool {
   460  	return len(x) == 0
   461  }
   462  
   463  func (x *StringArray) Add(v string) error {
   464  	if v == "" {
   465  		return nil
   466  	}
   467  	*x = append(*x, v)
   468  	return nil
   469  }
   470  
   471  func (x *StringArray) AddUnique(v string) error {
   472  	if v == "" {
   473  		return nil
   474  	}
   475  	if !x.Contains(v) {
   476  		*x = append(*x, v)
   477  	}
   478  	return nil
   479  }
   480  
   481  func (x *StringArray) Index(val string) int {
   482  	if val == "" {
   483  		return -1
   484  	}
   485  	for i, v := range *x {
   486  		if v == val {
   487  			return i
   488  		}
   489  	}
   490  	return -1
   491  }
   492  
   493  func (x *StringArray) Contains(v string) bool {
   494  	return x.Index(v) > -1
   495  }
   496  
   497  func (x *StringArray) Remove(v string) {
   498  	if v == "" {
   499  		return
   500  	}
   501  	if idx := x.Index(v); idx > -1 {
   502  		*x = append((*x)[:idx], (*x)[:idx+1]...)
   503  	}
   504  }
   505  
   506  func (x StringArray) Typ() ArrayType {
   507  	return ArrayTypeUnordered
   508  }
   509  
   510  func (x StringArray) MarshalXMP(e *Encoder, node *Node, m Model) error {
   511  	return MarshalArray(e, node, x.Typ(), x)
   512  }
   513  
   514  func (x *StringArray) UnmarshalXMP(d *Decoder, node *Node, m Model) error {
   515  	return UnmarshalArray(d, node, x.Typ(), x)
   516  }
   517  
   518  func (x *StringArray) UnmarshalText(data []byte) error {
   519  	list := strings.Split(strings.Replace(string(data), "\r\n", "\n", -1), "\n")
   520  	*x = append(*x, list...)
   521  	return nil
   522  }
   523  
   524  func (x StringArray) MarshalText() ([]byte, error) {
   525  	if len(x) == 0 {
   526  		return nil, nil
   527  	}
   528  	return []byte(strings.Join(x, "\n")), nil
   529  }
   530  
   531  func MarshalArray(e *Encoder, node *Node, typ ArrayType, items interface{}) error {
   532  
   533  	val := reflect.ValueOf(items)
   534  	kind := val.Kind()
   535  
   536  	if kind != reflect.Slice && kind != reflect.Array {
   537  		return fmt.Errorf("xmp: non-slice type passed to array marshal: %v %v", val.Type(), kind)
   538  	}
   539  
   540  	if val.Len() == 0 {
   541  		return nil
   542  	}
   543  
   544  	// output enclosing array type
   545  	arr := NewNode(xml.Name{Local: "rdf:" + string(typ)})
   546  	node.AddNode(arr)
   547  
   548  	// output array elements
   549  	for i, l := 0, val.Len(); i < l; i++ {
   550  		elem := NewNode(xml.Name{Local: "rdf:li"})
   551  		arr.Nodes = append(arr.Nodes, elem)
   552  
   553  		v := val.Index(i).Interface()
   554  
   555  		// add xml:Lang attribute to alternatives
   556  		if reflect.TypeOf(v) == reflect.TypeOf(AltItem{}) {
   557  			ai := v.(AltItem)
   558  			if typ == ArrayTypeAlternative {
   559  				if ai.IsDefault || val.Len() == 1 {
   560  					elem.AddStringAttr("xml:lang", "x-default")
   561  					if err := e.EncodeElement(ai.Value, elem); err != nil {
   562  						return err
   563  					}
   564  					// skip outputting default items twice when no lang is set
   565  					if ai.Lang == "" {
   566  						continue
   567  					}
   568  				}
   569  				if ai.IsDefault && ai.Lang != "" {
   570  					// add a second array node for a set language
   571  					elem := NewNode(xml.Name{Local: "rdf:li"})
   572  					arr.Nodes = append(arr.Nodes, elem)
   573  					elem.AddStringAttr("xml:lang", ai.Lang)
   574  					if err := e.EncodeElement(ai.Value, elem); err != nil {
   575  						return err
   576  					}
   577  					continue
   578  				}
   579  				// for any non-default items, add just xml:lang
   580  				if ai.Lang != "" {
   581  					elem.AddStringAttr("xml:lang", ai.Lang)
   582  				} else {
   583  					return fmt.Errorf("xmp: language required for alternative array item '%v'", node.FullName())
   584  				}
   585  			}
   586  
   587  			// write node contents to tree
   588  			if err := e.EncodeElement(ai.Value, elem); err != nil {
   589  				return err
   590  			}
   591  		} else {
   592  			// write to stream
   593  			if err := e.EncodeElement(v, elem); err != nil {
   594  				return err
   595  			}
   596  		}
   597  	}
   598  
   599  	return nil
   600  }
   601  
   602  func UnmarshalArray(d *Decoder, node *Node, typ ArrayType, out interface{}) error {
   603  	sliceValue := reflect.Indirect(reflect.ValueOf(out))
   604  	itemType := sliceValue.Type().Elem()
   605  
   606  	//
   607  	// LogDebugf("+++ Array start node %s\n", node.FullName())
   608  	//
   609  	if len(node.Nodes) != 1 {
   610  		return fmt.Errorf("xmp: invalid array %s: contains %d nodes", node.FullName(), len(node.Nodes))
   611  	}
   612  	arr := node.Nodes[0]
   613  
   614  	switch ArrayType(arr.Name()) {
   615  	default:
   616  		return fmt.Errorf("xmp: invalid array type %s", node.FullName())
   617  	case ArrayTypeOrdered,
   618  		ArrayTypeUnordered,
   619  		ArrayTypeAlternative:
   620  	}
   621  
   622  	for i, n := range arr.Nodes {
   623  		if n.FullName() != "rdf:li" {
   624  			return fmt.Errorf("xmp: invalid array element type %s", n.FullName())
   625  		}
   626  
   627  		val := reflect.New(itemType)
   628  		if itemType == reflect.TypeOf(AltItem{}) {
   629  			// Special unmarshalling for AltItems with lang attributes
   630  			//
   631  			i := val.Interface().(*AltItem)
   632  			if typ == ArrayTypeAlternative {
   633  				for _, v := range n.GetAttr("", "lang") {
   634  					switch v.Value {
   635  					case "x-default":
   636  						i.IsDefault = true
   637  					default:
   638  						i.Lang = v.Value
   639  					}
   640  				}
   641  			}
   642  			if err := d.DecodeElement(&i.Value, n); err != nil {
   643  				return err
   644  			}
   645  		} else {
   646  			// custom unmarshal for other types
   647  			//
   648  			// LogDebugf("++++ Array unmarshal custom type=%v\n", val.Type())
   649  			//
   650  			if err := d.unmarshal(val.Elem(), nil, n); err != nil {
   651  				return err
   652  			}
   653  		}
   654  		if sliceValue.Kind() == reflect.Array {
   655  			if sliceValue.Type().Len() <= i {
   656  				return fmt.Errorf("xmp: too many elements for fixed array type %s", n.FullName())
   657  			}
   658  			sliceValue.Index(i).Set(val.Elem())
   659  		} else {
   660  			sliceValue.Set(reflect.Append(sliceValue, val.Elem()))
   661  		}
   662  	}
   663  
   664  	return nil
   665  }