From 15cf52b558c4bb01f7ff917b7001630c32e1bc04 Mon Sep 17 00:00:00 2001 From: Dustin Oprea Date: Sat, 28 Jul 2018 17:00:47 -0400 Subject: [PATCH] ifd: Imp'd notion of IFD paths. --- ifd.go | 68 +++++++++++++++++++++++++++++++++++++++++-------- ifd_test.go | 73 ++++++++++++++++++++++++++++------------------------- 2 files changed, 96 insertions(+), 45 deletions(-) diff --git a/ifd.go b/ifd.go index 033f466..ea4925f 100644 --- a/ifd.go +++ b/ifd.go @@ -1,8 +1,8 @@ package exif import ( - "errors" "fmt" + "strings" "github.com/dsoprea/go-logging" ) @@ -25,10 +25,6 @@ const ( IfdRootId = 0x0000 ) -var ( - ErrIfdNotFound = errors.New("IFD not found") -) - type IfdNameAndIndex struct { Ii IfdIdentity Index int @@ -126,6 +122,7 @@ func (ii IfdIdentity) Id() int { type MappedIfd struct { ParentTagId uint16 + Path []string Name string TagId uint16 @@ -133,15 +130,22 @@ type MappedIfd struct { } func (mi *MappedIfd) String() string { - return fmt.Sprintf("MappedIfd<(0x%04X) [%s]>", mi.TagId, mi.Name) + pathPhrase := mi.PathPhrase() + return fmt.Sprintf("MappedIfd<(0x%04X) [%s] PATH=[%s]>", mi.TagId, mi.Name, pathPhrase) } +func (mi *MappedIfd) PathPhrase() string { + return strings.Join(mi.Path, "/") +} + +// IfdMapping describes all of the IFDs that we currently recognize. type IfdMapping struct { rootNode *MappedIfd } func NewIfdMapping() (ifdMapping *IfdMapping) { rootNode := &MappedIfd{ + Path: make([]string, 0), Children: make(map[uint16]*MappedIfd), } @@ -160,7 +164,7 @@ func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err er ptr := im.rootNode for _, tagId := range parentPlacement { if descendantPtr, found := ptr.Children[tagId]; found == false { - log.Panic(ErrIfdNotFound) + log.Panicf(fmt.Sprintf("ifd child with tag-ID (%04x) not registered", tagId)) } else { ptr = descendantPtr } @@ -169,6 +173,35 @@ func (im *IfdMapping) Get(parentPlacement []uint16) (childIfd *MappedIfd, err er return ptr, nil } +func (im *IfdMapping) GetWithPath(pathPhrase string) (mi *MappedIfd, err error) { + defer func() { + if state := recover(); state != nil { + err = log.Wrap(state.(error)) + } + }() + + path := strings.Split(pathPhrase, "/") + ptr := im.rootNode + + for _, name := range path { + var hit *MappedIfd + for _, mi := range ptr.Children { + if mi.Name == name { + hit = mi + break + } + } + + if hit == nil { + log.Panicf(fmt.Sprintf("ifd child with name [%s] not registered", name)) + } + + ptr = hit + } + + return ptr, nil +} + // Add puts the given IFD at the given position of the tree. The position of the // tree is referred to as the placement and is represented by a set of tag-IDs, // where the leftmost is the root tag and the tags going to the right are @@ -183,8 +216,16 @@ func (im *IfdMapping) Add(parentPlacement []uint16, tagId uint16, name string) ( ptr, err := im.Get(parentPlacement) log.PanicIf(err) + path := make([]string, len(parentPlacement)+1) + if len(parentPlacement) > 0 { + copy(path, ptr.Path) + } + + path[len(path)-1] = name + childIfd := &MappedIfd{ ParentTagId: ptr.TagId, + Path: path, Name: name, TagId: tagId, Children: make(map[uint16]*MappedIfd), @@ -199,7 +240,7 @@ func (im *IfdMapping) Add(parentPlacement []uint16, tagId uint16, name string) ( return nil } -func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input [][]*MappedIfd) (output [][]*MappedIfd, err error) { +func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input []string) (output []string, err error) { defer func() { if state := recover(); state != nil { err = log.Wrap(state.(error)) @@ -216,7 +257,12 @@ func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input [][]*MappedIfd) (ou stackCopy[len(stack)] = childIfd // Add to output, but don't include the obligatory root node. - output = append(output, stackCopy[1:]) + parts := make([]string, len(stackCopy)-1) + for i, mi := range stackCopy[1:] { + parts[i] = mi.Name + } + + output = append(output, strings.Join(parts, "/")) output, err = im.dumpLineages(stackCopy, output) log.PanicIf(err) @@ -225,15 +271,15 @@ func (im *IfdMapping) dumpLineages(stack []*MappedIfd, input [][]*MappedIfd) (ou return output, nil } -func (im *IfdMapping) DumpLineages() (output [][]*MappedIfd, err error) { +func (im *IfdMapping) DumpLineages() (output []string, err error) { defer func() { if state := recover(); state != nil { err = log.Wrap(state.(error)) } }() - output = make([][]*MappedIfd, 0) stack := []*MappedIfd{im.rootNode} + output = make([]string, 0) output, err = im.dumpLineages(stack, output) log.PanicIf(err) diff --git a/ifd_test.go b/ifd_test.go index 89e855f..80a647f 100644 --- a/ifd_test.go +++ b/ifd_test.go @@ -4,7 +4,6 @@ import ( "fmt" "reflect" "sort" - "strings" "testing" "github.com/dsoprea/go-logging" @@ -29,31 +28,23 @@ func TestIfdMapping_Add(t *testing.T) { log.PanicIf(err) lineages, err := im.DumpLineages() - lines := make([]string, len(lineages)) - for i, lineage := range lineages { - descriptions := make([]string, len(lineage)) - for i, mi := range lineage { - descriptions[i] = fmt.Sprintf("(0x%04x) [%s]", mi.TagId, mi.Name) - } + log.PanicIf(err) - lines[i] = strings.Join(descriptions, ", ") - } - - sort.Strings(lines) + sort.Strings(lineages) expected := []string{ - "(0x1111) [ifd0]", - "(0x1111) [ifd0], (0x4444) [ifd00]", - "(0x1111) [ifd0], (0x4444) [ifd00], (0x5555) [ifd000]", - "(0x2222) [ifd1]", - "(0x3333) [ifd2]", + "ifd0", + "ifd0/ifd00", + "ifd0/ifd00/ifd000", + "ifd1", + "ifd2", } - if reflect.DeepEqual(lines, expected) != true { + if reflect.DeepEqual(lineages, expected) != true { fmt.Printf("Actual:\n") fmt.Printf("\n") - for i, line := range lines { + for i, line := range lineages { fmt.Printf("(%d) %s\n", i, line) } @@ -77,30 +68,22 @@ func TestIfdMapping_LoadStandardIfds(t *testing.T) { log.PanicIf(err) lineages, err := im.DumpLineages() - lines := make([]string, len(lineages)) - for i, lineage := range lineages { - descriptions := make([]string, len(lineage)) - for i, mi := range lineage { - descriptions[i] = fmt.Sprintf("(0x%04x) [%s]", mi.TagId, mi.Name) - } + log.PanicIf(err) - lines[i] = strings.Join(descriptions, ", ") - } - - sort.Strings(lines) + sort.Strings(lineages) expected := []string{ - "(0x0000) [IFD]", - "(0x0000) [IFD], (0x8769) [Exif]", - "(0x0000) [IFD], (0x8769) [Exif], (0xa005) [Iop]", - "(0x0000) [IFD], (0x8825) [GPSInfo]", + "IFD", + "IFD/Exif", + "IFD/Exif/Iop", + "IFD/GPSInfo", } - if reflect.DeepEqual(lines, expected) != true { + if reflect.DeepEqual(lineages, expected) != true { fmt.Printf("Actual:\n") fmt.Printf("\n") - for i, line := range lines { + for i, line := range lineages { fmt.Printf("(%d) %s\n", i, line) } @@ -132,5 +115,27 @@ func TestIfdMapping_Get(t *testing.T) { t.Fatalf("Tag-ID not correct") } else if mi.Name != IfdIop { t.Fatalf("name not correct") + } else if mi.PathPhrase() != "IFD/Exif/Iop" { + t.Fatalf("path not correct") + } +} + +func TestIfdMapping_GetWithPath(t *testing.T) { + im := NewIfdMapping() + + err := LoadStandardIfds(im) + log.PanicIf(err) + + mi, err := im.GetWithPath("IFD/Exif/Iop") + log.PanicIf(err) + + if mi.ParentTagId != IfdExifId { + t.Fatalf("Parent tag-ID not correct") + } else if mi.TagId != IfdIopId { + t.Fatalf("Tag-ID not correct") + } else if mi.Name != IfdIop { + t.Fatalf("name not correct") + } else if mi.PathPhrase() != "IFD/Exif/Iop" { + t.Fatalf("path not correct") } }