ifd_tag_entry: Implemented Value() and ValueString() wrappers.

- type: Added `(TagType).Resolve()` to return the parsed, typed value.
pull/3/head
Dustin Oprea 2018-04-30 02:32:08 -04:00
parent 89cfa38cfc
commit 6ffca34f8e
2 changed files with 114 additions and 52 deletions

View File

@ -32,6 +32,37 @@ func (ite IfdTagEntry) String() string {
return fmt.Sprintf("IfdTagEntry<TAG-IFD=[%s] TAG-ID=(0x%02x) TAG-TYPE=[%s] UNIT-COUNT=(%d)>", ite.ChildIfdName, ite.TagId, TypeNames[ite.TagType], ite.UnitCount)
}
// ValueString renders a string from whatever the value in this tag is.
func (ite IfdTagEntry) ValueString(byteOrder binary.ByteOrder, addressableData []byte) (value string, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
vc := ValueContext{
UnitCount: ite.UnitCount,
ValueOffset: ite.ValueOffset,
RawValueOffset: ite.RawValueOffset,
AddressableData: addressableData,
}
if ite.TagType == TypeUndefined {
valueRaw, err = UndefinedValue(ite.Ii, ite.TagId, vc, byteOrder)
log.PanicIf(err)
value = fmt.Sprintf("%v", valueRaw)
} else {
tt := NewTagType(ite.TagType, byteOrder)
value, err = tt.ValueString(vc, false)
log.PanicIf(err)
}
return value, nil
}
// ValueBytes renders a specific list of bytes from the value in this tag.
func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteOrder) (value []byte, err error) {
defer func() {
if state := recover(); state != nil {
@ -39,6 +70,11 @@ func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteO
}
}()
// Return the exact bytes of the unknown-type value. Returning a string
// (`ValueString`) is easy because we can just pass everything to
// `Sprintf()`. Returning the raw, typed value (`Value`) is easy
// (obviously). However, here, in order to produce the list of bytes, we
// need to coerce whatever `UndefinedValue()` returns.
if ite.TagType == TypeUndefined {
valueContext := ValueContext{
UnitCount: ite.UnitCount,
@ -88,6 +124,34 @@ func (ite IfdTagEntry) ValueBytes(addressableData []byte, byteOrder binary.ByteO
return value, nil
}
// Value returns the specific, parsed, typed value from the tag.
func (ite IfdTagEntry) Value(byteOrder binary.ByteOrder, addressableData []byte) (value interface{}, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
vc := ValueContext{
UnitCount: ite.UnitCount,
ValueOffset: ite.ValueOffset,
RawValueOffset: ite.RawValueOffset,
AddressableData: addressableData,
}
if ite.TagType == TypeUndefined {
value, err = UndefinedValue(ite.Ii, ite.TagId, vc, byteOrder)
log.PanicIf(err)
} else {
tt := NewTagType(ite.TagType, byteOrder)
value, err = tt.Resolve(vc)
log.PanicIf(err)
}
return value, nil
}
// IfdTagEntryValueResolver instances know how to resolve the values for any
// tag for a particular EXIF block.
@ -108,6 +172,7 @@ func NewIfdTagEntryValueResolver(exifData []byte, byteOrder binary.ByteOrder) (i
}
}
// ValueBytes will resolve embedded or allocated data from the tag and return the raw bytes.
func (itevr *IfdTagEntryValueResolver) ValueBytes(ite *IfdTagEntry) (value []byte, err error) {
defer func() {
if state := recover(); state != nil {

101
type.go
View File

@ -416,8 +416,6 @@ func (tt TagType) ReadAsciiValue(valueContext ValueContext) (value string, err e
} else {
typeLogger.Debugf(nil, "Reading ASCII value (no-nul; at offset).")
fmt.Printf("Reading (%d) bytes of allocated ASCII from (%d) bytes of allocated data at offset (%d).\n", valueContext.UnitCount, len(valueContext.AddressableData), valueContext.ValueOffset)
value, err = tt.ParseAscii(valueContext.AddressableData[valueContext.ValueOffset:], valueContext.UnitCount)
log.PanicIf(err)
}
@ -576,8 +574,8 @@ func (tt TagType) ValueString(valueContext ValueContext, justFirst bool) (value
}
}()
// TODO(dustin): Implement Value(), below.
// valueRaw, err := tt.Value(valueContext)
// TODO(dustin): Implement Resolve(), below.
// valueRaw, err := tt.Resolve(valueContext)
// log.PanicIf(err)
typeId := tt.Type()
@ -676,59 +674,58 @@ func (tt TagType) ValueString(valueContext ValueContext, justFirst bool) (value
}
}
// // Value knows how to resolve the given value.
// //
// // Since this method lacks the information to process unknown-type tags (e.g.
// // byte-order, tag-ID, IFD type), it will return an error if attempted. See
// // `UndefinedValue()`.
// Value knows how to resolve the given value.
//
// Since this method lacks the information to process unknown-type tags (e.g.
// byte-order, tag-ID, IFD type), it will return an error if attempted. See
// `UndefinedValue()`.
func (tt TagType) Resolve(valueContext ValueContext) (value interface{}, err error) {
defer func() {
if state := recover(); state != nil {
err = log.Wrap(state.(error))
}
}()
// // TODO(dustin): !! Rename to Resolve() (to make clear that we're parsing not encoding)
typeId := tt.Type()
// func (tt TagType) Value(valueContext ValueContext) (value interface{}, err error) {
// defer func() {
// if state := recover(); state != nil {
// err = log.Wrap(state.(error))
// }
// }()
if typeId == TypeByte {
value, err = tt.ReadByteValues(valueContext)
log.PanicIf(err)
} else if typeId == TypeAscii {
value, err = tt.ReadAsciiValue(valueContext)
log.PanicIf(err)
} else if typeId == TypeAsciiNoNul {
value, err = tt.ReadAsciiNoNulValue(valueContext)
log.PanicIf(err)
} else if typeId == TypeShort {
value, err = tt.ReadShortValues(valueContext)
log.PanicIf(err)
} else if typeId == TypeLong {
value, err = tt.ReadLongValues(valueContext)
log.PanicIf(err)
} else if typeId == TypeRational {
value, err = tt.ReadRationalValues(valueContext)
log.PanicIf(err)
} else if typeId == TypeSignedLong {
value, err = tt.ReadSignedLongValues(valueContext)
log.PanicIf(err)
} else if typeId == TypeSignedRational {
value, err = tt.ReadSignedRationalValues(valueContext)
log.PanicIf(err)
} else if typeId == TypeUndefined {
log.Panicf("will not parse unknown-type value: %v", tt)
// typeId := tt.Type()
// Never called.
return nil, nil
} else {
log.Panicf("value of type (%d) [%s] is unparseable", typeId, tt)
// if typeId == TypeByte {
// value, err = tt.ReadByteValues(valueContext)
// log.PanicIf(err)
// } else if typeId == TypeAscii {
// value, err = tt.ReadAsciiValue(valueContext)
// log.PanicIf(err)
// } else if typeId == TypeAsciiNoNul {
// value, err = tt.ReadAsciiNoNulValue(valueContext)
// log.PanicIf(err)
// } else if typeId == TypeShort {
// value, err = tt.ReadShortValues(valueContext)
// log.PanicIf(err)
// } else if typeId == TypeLong {
// value, err = tt.ReadLongValues(valueContext)
// log.PanicIf(err)
// } else if typeId == TypeRational {
// value, err = tt.ReadRationalValues(valueContext)
// log.PanicIf(err)
// } else if typeId == TypeSignedLong {
// value, err = tt.ReadSignedLongValues(valueContext)
// log.PanicIf(err)
// } else if typeId == TypeSignedRational {
// value, err = tt.ReadSignedRationalValues(valueContext)
// log.PanicIf(err)
// } else if typeId == TypeUndefined {
// log.Panicf("will not parse unknown-type value: %v", tt)
// Never called.
return nil, nil
}
// // Never called.
// return "", nil
// } else {
// log.Panicf("value of type (%d) [%s] is unparseable", typeId, tt)
// // Never called.
// return "", nil
// }
// }
return value, nil
}
// ValueBytes knows how to encode the given value to a byte slice.