ifd: Imp'd notion of IFD paths.

pull/3/head
Dustin Oprea 2018-07-28 17:00:47 -04:00
parent 840d133d71
commit 15cf52b558
2 changed files with 96 additions and 45 deletions

68
ifd.go
View File

@ -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)

View File

@ -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")
}
}