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 }