From b1216c850e461d0483264f0abad69b7835dd597d Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 24 Sep 2021 14:39:29 +0200 Subject: [PATCH 01/23] skip unknown vertex and triangle attributes --- decoder.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/decoder.go b/decoder.go index 78df4b3..c6aa075 100644 --- a/decoder.go +++ b/decoder.go @@ -446,6 +446,9 @@ func (d *vertexDecoder) Start(attrs []spec.Attr) error { errs error ) for _, a := range attrs { + if a.Name.Space != "" { + continue + } val, err := strconv.ParseFloat(*(*string)(unsafe.Pointer(&a.Value)), 32) if err != nil { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, true)) @@ -505,6 +508,9 @@ func (d *triangleDecoder) Start(attrs []spec.Attr) error { ) for _, a := range attrs { + if a.Name.Space != "" { + continue + } required := true val, err := strconv.ParseUint(string(a.Value), 10, 32) switch a.Name.Local { @@ -735,7 +741,6 @@ func (d *topLevelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { type unknownAssetDecoder struct { spec.UnknownTokensDecoder resources *Resources - id string resource UnknownAsset } From 1d29ae4552b8b4f0731661ce0d6b02cbce1aaa07 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 24 Sep 2021 14:39:49 +0200 Subject: [PATCH 02/23] cleanup --- read_test.go | 10 ++-------- validate_test.go | 2 +- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/read_test.go b/read_test.go index 3e074a4..1ff7870 100644 --- a/read_test.go +++ b/read_test.go @@ -143,12 +143,6 @@ func (m *modelBuilder) withModel(unit string, lang string, thumbnail string) *mo return m } -func (m *modelBuilder) withEncoding(encode string) *modelBuilder { - m.str.WriteString(fmt.Sprintf(``, encode)) - m.str.WriteString("\n") - return m -} - func (m *modelBuilder) build(name string) *mockFile { if name == "" { name = "/3D/3dmodel.model" @@ -440,7 +434,7 @@ func TestDecoder_processRootModel(t *testing.T) { - + @@ -450,7 +444,7 @@ func TestDecoder_processRootModel(t *testing.T) { - + diff --git a/validate_test.go b/validate_test.go index 02eb750..bdcb2f0 100644 --- a/validate_test.go +++ b/validate_test.go @@ -56,7 +56,7 @@ func TestValidate(t *testing.T) { {ObjectID: 100}, {ObjectID: 1, Metadata: []Metadata{{Name: xml.Name{Local: "issue"}}}}, }}}, []string{ - fmt.Sprintf("Build: fake"), + "Build: fake", fmt.Sprintf("Build@Item#0: %v", &errors.MissingFieldError{Name: attrObjectID}), fmt.Sprintf("Build@Item#1: %v", errors.ErrOtherItem), fmt.Sprintf("Build@Item#2: %v", errors.ErrMissingResource), From 6c2b6197918e753cc01ed2c7754dc700987b3e80 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 24 Sep 2021 14:39:59 +0200 Subject: [PATCH 03/23] bump supported go versions --- .github/workflows/test.yml | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cdd04b9..35cabc6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,28 +4,28 @@ jobs: test: strategy: matrix: - go-version: [1.13.x, 1.14.x, 1.15.x] + go-version: [1.15.x, 1.16.x, 1.17.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: - - name: Install Go - uses: actions/setup-go@v2 - with: - go-version: ${{ matrix.go-version }} - - name: Checkout code - uses: actions/checkout@v2 - - name: Test - run: go test -race -covermode atomic -coverprofile profile.cov ./... - - name: Send coverage - uses: shogo82148/actions-goveralls@v1 - with: - path-to-profile: profile.cov - flag-name: Go-${{ matrix.go-version }} - parallel: true + - name: Install Go + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v2 + - name: Test + run: go test -race -covermode atomic -coverprofile profile.cov ./... + - name: Send coverage + uses: shogo82148/actions-goveralls@v1 + with: + path-to-profile: profile.cov + flag-name: Go-${{ matrix.go-version }} + parallel: true finish: needs: test runs-on: ubuntu-latest steps: - uses: shogo82148/actions-goveralls@v1 with: - parallel-finished: true \ No newline at end of file + parallel-finished: true From d2436c8bac5d4407c3eb9df5bfccb3a4ed64bac5 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 24 Sep 2021 14:43:07 +0200 Subject: [PATCH 04/23] remove unused method --- opc.go | 9 --------- 1 file changed, 9 deletions(-) diff --git a/opc.go b/opc.go index aeefc5e..c58e2b0 100644 --- a/opc.go +++ b/opc.go @@ -130,12 +130,3 @@ func findOPCFileFromName(name string, r *opc.Reader) (packageFile, bool) { } return nil, false } - -func findOPCFileURIFromRel(relType string, rels []*opc.Relationship) string { - for _, r := range rels { - if r.Type == relType { - return r.TargetURI - } - } - return "" -} From cf146b10478e5ea0836e8eb65d40ac152b551326 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Thu, 30 Sep 2021 16:21:01 +0200 Subject: [PATCH 05/23] improve attribute encoding --- beamlattice/beamlattice.go | 4 +- beamlattice/decoder.go | 16 +-- core.go | 42 ++------ core_test.go | 8 +- decoder.go | 206 ++++++++++++++++++++---------------- encoder.go | 29 ++--- encoder_test.go | 23 ++-- materials/decoder.go | 20 ++-- materials/materials.go | 3 +- materials/materials_test.go | 6 -- production/decoder.go | 101 +++++++++--------- production/decoder_test.go | 11 +- production/encoder.go | 42 +++----- production/encoder_test.go | 11 +- production/production.go | 12 ++- production/validate_test.go | 35 +++--- read.go | 2 +- read_test.go | 67 ++++++------ slices/decoder.go | 42 +++----- slices/decoder_test.go | 3 +- slices/encoder.go | 11 +- slices/encoder_test.go | 3 +- slices/slices.go | 6 +- slices/validate_test.go | 19 ++-- spec.go | 34 ------ spec/spec.go | 101 +++++++++++------- spec/unknown.go | 73 +++++++++++++ validate.go | 8 +- validate_test.go | 5 +- 29 files changed, 499 insertions(+), 444 deletions(-) create mode 100644 spec/unknown.go diff --git a/beamlattice/beamlattice.go b/beamlattice/beamlattice.go index 0f3505f..6bbabf5 100644 --- a/beamlattice/beamlattice.go +++ b/beamlattice/beamlattice.go @@ -5,7 +5,9 @@ package beamlattice import ( "errors" + "github.com/hpinc/go3mf" + "github.com/hpinc/go3mf/spec" ) // Namespace is the canonical name of this extension. @@ -26,7 +28,7 @@ var ( ) func init() { - go3mf.Register(Namespace, Spec{}) + spec.Register(Namespace, Spec{}) } type Spec struct{} diff --git a/beamlattice/decoder.go b/beamlattice/decoder.go index 7bf3f66..7efff71 100644 --- a/beamlattice/decoder.go +++ b/beamlattice/decoder.go @@ -12,7 +12,7 @@ import ( "github.com/hpinc/go3mf/spec" ) -func (Spec) DecodeAttribute(interface{}, spec.Attr) error { +func (Spec) NewAttr3MF(string) spec.Attr3MF { return nil } @@ -28,7 +28,7 @@ type beamLatticeDecoder struct { mesh *go3mf.Mesh } -func (d *beamLatticeDecoder) Start(attrs []spec.Attr) error { +func (d *beamLatticeDecoder) Start(attrs []spec.XMLAttr) error { var errs error beamLattice := new(BeamLattice) d.mesh.Any = append(d.mesh.Any, beamLattice) @@ -102,7 +102,7 @@ type beamsDecoder struct { beamDecoder beamDecoder } -func (d *beamsDecoder) Start(_ []spec.Attr) error { +func (d *beamsDecoder) Start(_ []spec.XMLAttr) error { d.beamDecoder.mesh = d.mesh return nil } @@ -119,7 +119,7 @@ type beamDecoder struct { mesh *go3mf.Mesh } -func (d *beamDecoder) Start(attrs []spec.Attr) error { +func (d *beamDecoder) Start(attrs []spec.XMLAttr) error { var ( beam Beam hasCap1, hasCap2 bool @@ -212,7 +212,7 @@ func (d *beamSetDecoder) End() { beamLattice.BeamSets = append(beamLattice.BeamSets, d.beamSet) } -func (d *beamSetDecoder) Start(attrs []spec.Attr) error { +func (d *beamSetDecoder) Start(attrs []spec.XMLAttr) error { d.beamRefDecoder.beamSet = &d.beamSet for _, a := range attrs { if a.Name.Space != "" { @@ -244,7 +244,7 @@ type beamRefDecoder struct { beamSet *BeamSet } -func (d *beamRefDecoder) Start(attrs []spec.Attr) error { +func (d *beamRefDecoder) Start(attrs []spec.XMLAttr) error { var ( val uint64 errs error @@ -269,5 +269,5 @@ func (d *beamRefDecoder) Start(attrs []spec.Attr) error { type baseDecoder struct { } -func (d *baseDecoder) Start([]spec.Attr) error { return nil } -func (d *baseDecoder) End() {} +func (d *baseDecoder) Start([]spec.XMLAttr) error { return nil } +func (d *baseDecoder) End() {} diff --git a/core.go b/core.go index 5b33109..c46d3e7 100644 --- a/core.go +++ b/core.go @@ -123,7 +123,7 @@ type Relationship struct { // Build contains one or more items to manufacture as part of processing the job. type Build struct { Items []*Item - AnyAttr AnyAttr + AnyAttr spec.AnyAttr } // The Resources element acts as the root element of a library of constituent @@ -131,7 +131,7 @@ type Build struct { type Resources struct { Assets []Asset Objects []*Object - AnyAttr AnyAttr + AnyAttr spec.AnyAttr } // UnusedID returns the lowest unused ID. @@ -221,7 +221,7 @@ type Model struct { RootRelationships []Relationship Relationships []Relationship Any Any - AnyAttr AnyAttr + AnyAttr spec.AnyAttr } // PathOrDefault returns Path if not empty, else DefaultModelPath. @@ -338,14 +338,14 @@ func (m *Model) WalkObjects(fn func(string, *Object) error) error { type Base struct { Name string Color color.RGBA - AnyAttr AnyAttr + AnyAttr spec.AnyAttr } // BaseMaterials defines a slice of Base. type BaseMaterials struct { ID uint32 Materials []Base - AnyAttr AnyAttr + AnyAttr spec.AnyAttr } // Len returns the materials count. @@ -364,7 +364,7 @@ type Item struct { Transform Matrix PartNumber string Metadata []Metadata - AnyAttr AnyAttr + AnyAttr spec.AnyAttr } // ObjectPath search an extension attribute with an ObjectPath @@ -399,7 +399,7 @@ type Object struct { Metadata []Metadata Mesh *Mesh Components *Components - AnyAttr AnyAttr + AnyAttr spec.AnyAttr } func (o *Object) boundingBox(m *Model, path string) Box { @@ -424,14 +424,14 @@ func (o *Object) boundingBox(m *Model, path string) Box { // A Components is an in memory representation of the 3MF components. type Components struct { Component []*Component - AnyAttr AnyAttr + AnyAttr spec.AnyAttr } // A Component is an in memory representation of the 3MF component. type Component struct { ObjectID uint32 Transform Matrix - AnyAttr AnyAttr + AnyAttr spec.AnyAttr } // ObjectPath search an extension attribute with an ObjectPath @@ -461,6 +461,7 @@ type Triangle struct { V1, V2, V3 uint32 PID uint32 P1, P2, P3 uint32 + AnyAttr spec.AnyAttr } // A Mesh is an in memory representation of the 3MF mesh object. @@ -470,7 +471,7 @@ type Triangle struct { type Mesh struct { Vertices []Point3D Triangles []Triangle - AnyAttr AnyAttr + AnyAttr spec.AnyAttr Any Any } @@ -545,27 +546,6 @@ func newUnits(s string) (u Units, ok bool) { return } -// AnyAttr is an extension point containing information. -type AnyAttr []spec.MarshalerAttr - -func (any *AnyAttr) AddUnknownAttr(a spec.Attr) { - ua := any.GetUnknownAttr() - if ua == nil { - ua = &spec.UnknownAttrs{} - *any = append(*any, ua) - } - *ua = append(*ua, xml.Attr{Name: a.Name, Value: string(a.Value)}) -} - -func (any AnyAttr) GetUnknownAttr() *spec.UnknownAttrs { - for _, a := range any { - if a, ok := a.(*spec.UnknownAttrs); ok { - return a - } - } - return nil -} - // Any is an extension point containing information. type Any []spec.Marshaler diff --git a/core_test.go b/core_test.go index 8ea9ed1..c5c1bbe 100644 --- a/core_test.go +++ b/core_test.go @@ -336,8 +336,8 @@ func TestComponent_ObjectPath(t *testing.T) { want string }{ {"emptyattr", &Component{}, args{"/other.model"}, "/other.model"}, - {"emptypath", &Component{AnyAttr: AnyAttr{&fakeAttr{}}}, args{"/other.model"}, "/other.model"}, - {"emptyattr", &Component{AnyAttr: AnyAttr{&fakeAttr{Value: "/3dmodel.model"}}}, args{"/other.model"}, "/3dmodel.model"}, + {"emptypath", &Component{AnyAttr: spec.AnyAttr{&fakeAttr{}}}, args{"/other.model"}, "/other.model"}, + {"emptyattr", &Component{AnyAttr: spec.AnyAttr{&fakeAttr{Value: "/3dmodel.model"}}}, args{"/other.model"}, "/3dmodel.model"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -355,8 +355,8 @@ func TestItem_ObjectPath(t *testing.T) { want string }{ {"emptyattr", &Item{}, ""}, - {"emptypath", &Item{AnyAttr: AnyAttr{&fakeAttr{}}}, ""}, - {"emptyattr", &Item{AnyAttr: AnyAttr{&fakeAttr{Value: "/3dmodel.model"}}}, "/3dmodel.model"}, + {"emptypath", &Item{AnyAttr: spec.AnyAttr{&fakeAttr{}}}, ""}, + {"emptyattr", &Item{AnyAttr: spec.AnyAttr{&fakeAttr{Value: "/3dmodel.model"}}}, "/3dmodel.model"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/decoder.go b/decoder.go index c6aa075..96f66cd 100644 --- a/decoder.go +++ b/decoder.go @@ -36,7 +36,7 @@ func (d *modelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { child = &metadataDecoder{metadatas: &d.model.Metadata, model: d.model} } } - } else if ext, ok := loadExtension(name.Space); ok { + } else if ext, ok := spec.LoadExtension(name.Space); ok { child = ext.CreateElementDecoder(d.model, name.Local) } else { child = &anyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.model.Any} @@ -44,7 +44,7 @@ func (d *modelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *modelDecoder) Start(attrs []spec.Attr) (err error) { +func (d *modelDecoder) Start(attrs []spec.XMLAttr) (err error) { if !d.isRoot { return } @@ -79,7 +79,7 @@ func (d *modelDecoder) Start(attrs []spec.Attr) (err error) { return } -func (d *modelDecoder) noCoreAttribute(a spec.Attr) (err error) { +func (d *modelDecoder) noCoreAttribute(a spec.XMLAttr) (err error) { switch a.Name.Space { case nsXML: if a.Name.Local == attrLang { @@ -92,11 +92,12 @@ func (d *modelDecoder) noCoreAttribute(a spec.Attr) (err error) { IsRequired: false, }) default: - if ext, ok := loadExtension(a.Name.Space); ok { - err = specerr.Append(err, ext.DecodeAttribute(d.model, a)) - } else { - d.model.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = d.model.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrModel) + d.model.AnyAttr = append(d.model.AnyAttr, attr) } + err = specerr.Append(err, attr.Unmarshal3MFAttr(a)) } return } @@ -130,7 +131,7 @@ func (d *metadataDecoder) namespace(local string) (string, bool) { return "", false } -func (d *metadataDecoder) Start(attrs []spec.Attr) error { +func (d *metadataDecoder) Start(attrs []spec.XMLAttr) error { for _, a := range attrs { if a.Name.Space != "" { continue @@ -181,14 +182,15 @@ func (d *buildDecoder) Wrap(err error) error { return specerr.Wrap(err, d.build) } -func (d *buildDecoder) Start(attrs []spec.Attr) error { +func (d *buildDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { - if ext, ok := loadExtension(a.Name.Space); ok { - errs = specerr.Append(errs, ext.DecodeAttribute(d.build, a)) - } else { - d.build.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = d.build.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrBuild) + d.build.AnyAttr = append(d.build.AnyAttr, attr) } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } if errs != nil { return d.Wrap(errs) @@ -218,15 +220,18 @@ func (d *buildItemDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *buildItemDecoder) Start(attrs []spec.Attr) error { +func (d *buildItemDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { if a.Name.Space == "" { errs = specerr.Append(errs, d.parseCoreAttr(a)) - } else if ext, ok := loadExtension(a.Name.Space); ok { - errs = specerr.Append(errs, ext.DecodeAttribute(&d.item, a)) } else { - d.item.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = d.item.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrItem) + d.item.AnyAttr = append(d.item.AnyAttr, attr) + } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } } if errs != nil { @@ -235,7 +240,7 @@ func (d *buildItemDecoder) Start(attrs []spec.Attr) error { return errs } -func (d *buildItemDecoder) parseCoreAttr(a spec.Attr) (errs error) { +func (d *buildItemDecoder) parseCoreAttr(a spec.XMLAttr) (errs error) { switch a.Name.Local { case attrObjectID: val, err := strconv.ParseUint(string(a.Value), 10, 32) @@ -261,14 +266,15 @@ type resourceDecoder struct { resources *Resources } -func (d *resourceDecoder) Start(attrs []spec.Attr) error { +func (d *resourceDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { - if ext, ok := loadExtension(a.Name.Space); ok { - errs = specerr.Append(errs, ext.DecodeAttribute(d.resources, a)) - } else { - d.resources.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = d.resources.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrResources) + d.resources.AnyAttr = append(d.resources.AnyAttr, attr) } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } if errs != nil { return specerr.Wrap(errs, d.resources) @@ -288,7 +294,7 @@ func (d *resourceDecoder) Child(name xml.Name) (child spec.ElementDecoder) { case attrBaseMaterials: child = &baseMaterialsDecoder{resources: d.resources} } - } else if ext, ok := loadExtension(name.Space); ok { + } else if ext, ok := spec.LoadExtension(name.Space); ok { child = ext.CreateElementDecoder(d.resources, name.Local) } else { child = &unknownAssetDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, resources: d.resources} @@ -318,7 +324,7 @@ func (d *baseMaterialsDecoder) Child(name xml.Name) (child spec.ElementDecoder) return } -func (d *baseMaterialsDecoder) Start(attrs []spec.Attr) error { +func (d *baseMaterialsDecoder) Start(attrs []spec.XMLAttr) error { var errs error d.baseMaterialDecoder.resource = &d.resource for _, a := range attrs { @@ -330,10 +336,13 @@ func (d *baseMaterialsDecoder) Start(attrs []spec.Attr) error { } d.resource.ID = uint32(id) } - } else if ext, ok := loadExtension(a.Name.Space); ok { - errs = specerr.Append(errs, ext.DecodeAttribute(&d.resource, a)) } else { - d.resource.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = d.resource.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrBaseMaterials) + d.resource.AnyAttr = append(d.resource.AnyAttr, attr) + } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } } if errs != nil { @@ -347,7 +356,7 @@ type baseMaterialDecoder struct { resource *BaseMaterials } -func (d *baseMaterialDecoder) Start(attrs []spec.Attr) error { +func (d *baseMaterialDecoder) Start(attrs []spec.XMLAttr) error { var ( base Base errs error @@ -364,10 +373,13 @@ func (d *baseMaterialDecoder) Start(attrs []spec.Attr) error { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, true)) } } - } else if ext, ok := loadExtension(a.Name.Space); ok { - errs = specerr.Append(errs, ext.DecodeAttribute(&base, a)) } else { - base.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = base.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrBase) + base.AnyAttr = append(base.AnyAttr, attr) + } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } } d.resource.Materials = append(d.resource.Materials, base) @@ -382,15 +394,16 @@ type meshDecoder struct { resource *Object } -func (d *meshDecoder) Start(attrs []spec.Attr) error { +func (d *meshDecoder) Start(attrs []spec.XMLAttr) error { d.resource.Mesh = new(Mesh) var errs error for _, a := range attrs { - if ext, ok := loadExtension(a.Name.Space); ok { - errs = specerr.Append(errs, ext.DecodeAttribute(d.resource.Mesh, a)) - } else { - d.resource.Mesh.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = d.resource.Mesh.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrMesh) + d.resource.Mesh.AnyAttr = append(d.resource.Mesh.AnyAttr, attr) } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } if errs != nil { return specerr.Wrap(errs, d.resource.Mesh) @@ -409,7 +422,7 @@ func (d *meshDecoder) Child(name xml.Name) (child spec.ElementDecoder) { } else if name.Local == attrTriangles { child = &trianglesDecoder{resource: d.resource} } - } else if ext, ok := loadExtension(name.Space); ok { + } else if ext, ok := spec.LoadExtension(name.Space); ok { child = ext.CreateElementDecoder(d.resource.Mesh, name.Local) } else { child = &anyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.resource.Mesh.Any} @@ -423,7 +436,7 @@ type verticesDecoder struct { vertexDecoder vertexDecoder } -func (d *verticesDecoder) Start(_ []spec.Attr) error { +func (d *verticesDecoder) Start(_ []spec.XMLAttr) error { d.vertexDecoder.mesh = d.mesh return nil } @@ -440,7 +453,7 @@ type vertexDecoder struct { mesh *Mesh } -func (d *vertexDecoder) Start(attrs []spec.Attr) error { +func (d *vertexDecoder) Start(attrs []spec.XMLAttr) error { var ( x, y, z float32 errs error @@ -475,7 +488,7 @@ type trianglesDecoder struct { triangleDecoder triangleDecoder } -func (d *trianglesDecoder) Start(_ []spec.Attr) error { +func (d *trianglesDecoder) Start(_ []spec.XMLAttr) error { d.triangleDecoder.mesh = d.resource.Mesh d.triangleDecoder.defaultPropertyID = d.resource.PID d.triangleDecoder.defaultPropertyIndex = d.resource.PIndex @@ -499,7 +512,7 @@ type triangleDecoder struct { defaultPropertyIndex, defaultPropertyID uint32 } -func (d *triangleDecoder) Start(attrs []spec.Attr) error { +func (d *triangleDecoder) Start(attrs []spec.XMLAttr) error { var ( t Triangle pid, p1, p2, p3 uint32 @@ -508,37 +521,43 @@ func (d *triangleDecoder) Start(attrs []spec.Attr) error { ) for _, a := range attrs { - if a.Name.Space != "" { - continue - } - required := true - val, err := strconv.ParseUint(string(a.Value), 10, 32) - switch a.Name.Local { - case attrV1: - t.V1 = uint32(val) - case attrV2: - t.V2 = uint32(val) - case attrV3: - t.V3 = uint32(val) - case attrPID: - pid = uint32(val) - hasPID = true - required = false - case attrP1: - p1 = uint32(val) - hasP1 = true - required = false - case attrP2: - p2 = uint32(val) - hasP2 = true - required = false - case attrP3: - p3 = uint32(val) - hasP3 = true - required = false - } - if err != nil { - errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, required)) + if a.Name.Space == "" { + required := true + val, err := strconv.ParseUint(string(a.Value), 10, 32) + switch a.Name.Local { + case attrV1: + t.V1 = uint32(val) + case attrV2: + t.V2 = uint32(val) + case attrV3: + t.V3 = uint32(val) + case attrPID: + pid = uint32(val) + hasPID = true + required = false + case attrP1: + p1 = uint32(val) + hasP1 = true + required = false + case attrP2: + p2 = uint32(val) + hasP2 = true + required = false + case attrP3: + p3 = uint32(val) + hasP3 = true + required = false + } + if err != nil { + errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, required)) + } + } else { + var attr spec.Attr3MF + if attr = t.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrTriangle) + t.AnyAttr = append(t.AnyAttr, attr) + } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } } @@ -573,15 +592,18 @@ func (d *objectDecoder) End() { d.resources.Objects = append(d.resources.Objects, &d.resource) } -func (d *objectDecoder) Start(attrs []spec.Attr) error { +func (d *objectDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { if a.Name.Space == "" { errs = specerr.Append(errs, d.parseCoreAttr(a)) - } else if ext, ok := loadExtension(a.Name.Space); ok { - errs = specerr.Append(errs, ext.DecodeAttribute(&d.resource, a)) } else { - d.resource.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = d.resource.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrObject) + d.resource.AnyAttr = append(d.resource.AnyAttr, attr) + } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } } if errs != nil { @@ -607,7 +629,7 @@ func (d *objectDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *objectDecoder) parseCoreAttr(a spec.Attr) (errs error) { +func (d *objectDecoder) parseCoreAttr(a spec.XMLAttr) (errs error) { switch a.Name.Local { case attrID: id, err := strconv.ParseUint(string(a.Value), 10, 32) @@ -649,17 +671,18 @@ type componentsDecoder struct { componentDecoder componentDecoder } -func (d *componentsDecoder) Start(attrs []spec.Attr) error { +func (d *componentsDecoder) Start(attrs []spec.XMLAttr) error { var errs error components := new(Components) d.componentDecoder.resource = d.resource for _, a := range attrs { - if ext, ok := loadExtension(a.Name.Space); ok { - errs = specerr.Append(errs, ext.DecodeAttribute(components, a)) - } else { - components.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = components.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrComponents) + components.AnyAttr = append(components.AnyAttr, attr) } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } d.resource.Components = components if errs != nil { @@ -684,7 +707,7 @@ type componentDecoder struct { resource *Object } -func (d *componentDecoder) Start(attrs []spec.Attr) error { +func (d *componentDecoder) Start(attrs []spec.XMLAttr) error { var ( component Component errs error @@ -704,10 +727,13 @@ func (d *componentDecoder) Start(attrs []spec.Attr) error { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, false)) } } - } else if ext, ok := loadExtension(a.Name.Space); ok { - errs = specerr.Append(errs, ext.DecodeAttribute(&component, a)) } else { - component.AnyAttr.AddUnknownAttr(a) + var attr spec.Attr3MF + if attr = component.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttr3MF(a.Name.Space, attrComponent) + component.AnyAttr = append(component.AnyAttr, attr) + } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } } d.resource.Components.Component = append(d.resource.Components.Component, &component) @@ -720,8 +746,8 @@ func (d *componentDecoder) Start(attrs []spec.Attr) error { type baseDecoder struct { } -func (d *baseDecoder) Start([]spec.Attr) error { return nil } -func (d *baseDecoder) End() {} +func (d *baseDecoder) Start([]spec.XMLAttr) error { return nil } +func (d *baseDecoder) End() {} type topLevelDecoder struct { baseDecoder @@ -744,7 +770,7 @@ type unknownAssetDecoder struct { resource UnknownAsset } -func (d *unknownAssetDecoder) Start(attrs []spec.Attr) (errs error) { +func (d *unknownAssetDecoder) Start(attrs []spec.XMLAttr) (errs error) { d.UnknownTokensDecoder.Start(attrs) for _, a := range attrs { if a.Name.Space == "" && a.Name.Local == attrID { diff --git a/encoder.go b/encoder.go index ac20fe4..f584680 100644 --- a/encoder.go +++ b/encoder.go @@ -234,7 +234,7 @@ func (e *Encoder) modelToken(x spec.Encoder, m *Model, isRoot bool) (xml.StartEl attrs = append(attrs, xml.Attr{Name: xml.Name{Local: attrReqExt}, Value: strings.Join(exts, " ")}) } tm := xml.StartElement{Name: xml.Name{Local: attrModel}, Attr: attrs} - m.AnyAttr.encode(x, &tm) + m.AnyAttr.Marshal3MFAttr(x, &tm) return tm, nil } @@ -280,7 +280,7 @@ func (e *Encoder) writeMetadataGroup(x spec.Encoder, m []Metadata) { func (e *Encoder) writeBuild(x spec.Encoder, m *Model) { xb := xml.StartElement{Name: xml.Name{Local: attrBuild}} - m.Build.AnyAttr.encode(x, &xb) + m.Build.AnyAttr.Marshal3MFAttr(x, &xb) x.EncodeToken(xb) x.SetAutoClose(true) for _, item := range m.Build.Items { @@ -297,7 +297,7 @@ func (e *Encoder) writeBuild(x spec.Encoder, m *Model) { Name: xml.Name{Local: attrPartNumber}, Value: item.PartNumber, }) } - item.AnyAttr.encode(x, &xi) + item.AnyAttr.Marshal3MFAttr(x, &xi) if len(item.Metadata) != 0 { x.SetAutoClose(false) x.EncodeToken(xi) @@ -314,7 +314,7 @@ func (e *Encoder) writeBuild(x spec.Encoder, m *Model) { func (e *Encoder) writeResources(x spec.Encoder, rs *Resources) error { xt := xml.StartElement{Name: xml.Name{Local: attrResources}} - rs.AnyAttr.encode(x, &xt) + rs.AnyAttr.Marshal3MFAttr(x, &xt) x.EncodeToken(xt) for _, r := range rs.Assets { if r, ok := r.(spec.Marshaler); ok { @@ -391,7 +391,7 @@ func (e *Encoder) writeObject(x spec.Encoder, r *Object) { }) } } - r.AnyAttr.encode(x, &xo) + r.AnyAttr.Marshal3MFAttr(x, &xo) x.EncodeToken(xo) if len(r.Metadata) != 0 { @@ -408,7 +408,7 @@ func (e *Encoder) writeObject(x spec.Encoder, r *Object) { func (e *Encoder) writeComponents(x spec.Encoder, comps *Components) { xcs := xml.StartElement{Name: xml.Name{Local: attrComponents}} - comps.AnyAttr.encode(x, &xcs) + comps.AnyAttr.Marshal3MFAttr(x, &xcs) x.EncodeToken(xcs) x.SetAutoClose(true) for _, c := range comps.Component { @@ -420,7 +420,7 @@ func (e *Encoder) writeComponents(x spec.Encoder, comps *Components) { if c.HasTransform() { xt.Attr = append(xt.Attr, xml.Attr{Name: xml.Name{Local: attrTransform}, Value: c.Transform.String()}) } - c.AnyAttr.encode(x, &xt) + c.AnyAttr.Marshal3MFAttr(x, &xt) x.EncodeToken(xt) } x.SetAutoClose(false) @@ -487,6 +487,7 @@ func (e *Encoder) writeTriangles(x spec.Encoder, r *Object, m *Mesh) { start.Attr = attrs[:5] } } + t.AnyAttr.Marshal3MFAttr(x, &start) x.EncodeToken(start) } x.SetSkipAttrEscape(false) @@ -496,7 +497,7 @@ func (e *Encoder) writeTriangles(x spec.Encoder, r *Object, m *Mesh) { func (e *Encoder) writeMesh(x spec.Encoder, r *Object, m *Mesh) { xm := xml.StartElement{Name: xml.Name{Local: attrMesh}} - m.AnyAttr.encode(x, &xm) + m.AnyAttr.Marshal3MFAttr(x, &xm) x.EncodeToken(xm) e.writeVertices(x, m) @@ -510,7 +511,7 @@ func (r *BaseMaterials) Marshal3MF(x spec.Encoder) error { xt := xml.StartElement{Name: xml.Name{Local: attrBaseMaterials}, Attr: []xml.Attr{ {Name: xml.Name{Local: attrID}, Value: strconv.FormatUint(uint64(r.ID), 10)}, }} - r.AnyAttr.encode(x, &xt) + r.AnyAttr.Marshal3MFAttr(x, &xt) x.EncodeToken(xt) x.SetAutoClose(true) for _, ma := range r.Materials { @@ -521,7 +522,7 @@ func (r *BaseMaterials) Marshal3MF(x spec.Encoder) error { {Name: xml.Name{Local: attrDisplayColor}, Value: spec.FormatRGBA(ma.Color)}, }, } - ma.AnyAttr.encode(x, &start) + ma.AnyAttr.Marshal3MFAttr(x, &start) x.EncodeToken(start) } x.SetAutoClose(false) @@ -529,14 +530,6 @@ func (r *BaseMaterials) Marshal3MF(x spec.Encoder) error { return nil } -func (e AnyAttr) encode(x spec.Encoder, start *xml.StartElement) { - for _, ext := range e { - if att, err := ext.Marshal3MFAttr(x); err == nil { - start.Attr = append(start.Attr, att...) - } - } -} - func (e Any) encode(x spec.Encoder) error { for _, ext := range e { if err := ext.Marshal3MF(x); err == nil { diff --git a/encoder_test.go b/encoder_test.go index b2e7dfd..23f1822 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -17,12 +17,6 @@ import ( "github.com/stretchr/testify/mock" ) -func (f *fakeAttr) Marshal3MFAttr(_ spec.Encoder) ([]xml.Attr, error) { - return []xml.Attr{ - {Name: xml.Name{Space: fakeExtension, Local: "value"}, Value: f.Value}, - }, nil -} - // Marshal3MF encodes the resource. func (f *fakeAsset) Marshal3MF(x spec.Encoder) error { xs := xml.StartElement{Name: xml.Name{Space: fakeExtension, Local: "fakeasset"}, Attr: []xml.Attr{ @@ -47,11 +41,11 @@ func (m *mockPackagePart) AddRelationship(args0 Relationship) { } func TestMarshalModel(t *testing.T) { - Register(fakeSpec.Namespace, new(qmExtension)) + spec.Register(fakeSpec.Namespace, new(qmExtension)) m := &Model{ Units: UnitMillimeter, Language: "en-US", Path: "/3D/3dmodel.model", Thumbnail: "/thumbnail.png", Extensions: []Extension{fakeSpec, fooSpec}, - AnyAttr: AnyAttr{&fakeAttr{Value: "model_fake"}, &spec.UnknownAttrs{{Name: fooName, Value: "foo1"}}}, + AnyAttr: spec.AnyAttr{&fakeAttr{Value: "model_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo1"}}}}, Any: Any{spec.UnknownTokens{ xml.StartElement{Name: fooName}, xml.EndElement{Name: fooName}, @@ -65,13 +59,14 @@ func TestMarshalModel(t *testing.T) { xml.EndElement{Name: fooName}, }}, &BaseMaterials{ID: 5, Materials: []Base{ - {Name: "Blue PLA", Color: color.RGBA{0, 0, 255, 255}, AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "foo6"}}}}, + {Name: "Blue PLA", Color: color.RGBA{0, 0, 255, 255}, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo6"}}}}}, {Name: "Red ABS", Color: color.RGBA{255, 0, 0, 255}}, - }, AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "foo2"}}}}, &fakeAsset{ID: 25}}, + }, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo2"}}}}}, + &fakeAsset{ID: 25}}, Objects: []*Object{ { ID: 8, Name: "Box 1", PartNumber: "11111111-1111-1111-1111-111111111111", Thumbnail: "/a.png", - AnyAttr: AnyAttr{&fakeAttr{Value: "object_fake"}, &spec.UnknownAttrs{{Name: fooName, Value: "foo3"}}}, + AnyAttr: spec.AnyAttr{&fakeAttr{Value: "object_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo3"}}}}, PID: 1, PIndex: 1, Type: ObjectTypeModel, Mesh: &Mesh{ Any: Any{spec.UnknownTokens{ xml.StartElement{Name: fooName}, @@ -100,18 +95,18 @@ func TestMarshalModel(t *testing.T) { ID: 20, Type: ObjectTypeSupport, Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}, {Name: xml.Name{Space: "qm", Local: "CustomMetadata4"}, Type: "xs:boolean", Value: "2"}}, Components: &Components{Component: []*Component{{ObjectID: 8, Transform: Matrix{3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, -66.4, -87.1, 8.8, 1}, - AnyAttr: AnyAttr{&fakeAttr{Value: "component_fake"}, &spec.UnknownAttrs{{Name: fooName, Value: "foo8"}}}}}}, + AnyAttr: spec.AnyAttr{&fakeAttr{Value: "component_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo8"}}}}}}}, }, }, }, Build: Build{ - AnyAttr: AnyAttr{&fakeAttr{Value: "build_fake"}, &spec.UnknownAttrs{{Name: fooName, Value: "foo4"}, {Name: fooName, Value: "foo6"}}}, + AnyAttr: spec.AnyAttr{&fakeAttr{Value: "build_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo4"}, {Name: fooName, Value: "foo6"}}}}, Items: []*Item{ { ObjectID: 20, PartNumber: "bob", Transform: Matrix{1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, -66.4, -87.1, 8.8, 1}, Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}}, }, - {ObjectID: 21, AnyAttr: AnyAttr{&fakeAttr{Value: "item_fake"}, &spec.UnknownAttrs{{Name: fooName, Value: "foo5"}}}}, + {ObjectID: 21, AnyAttr: spec.AnyAttr{&fakeAttr{Value: "item_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo5"}}}}}, }}, Metadata: []Metadata{ {Name: xml.Name{Local: "Application"}, Value: "go3mf app"}, {Name: xml.Name{Space: "qm", Local: "CustomMetadata1"}, Preserve: true, Type: "xs:string", Value: "CE8A91FB-C44E-4F00-B634-BAA411465F6A"}, diff --git a/materials/decoder.go b/materials/decoder.go index 71555a8..6b78f38 100644 --- a/materials/decoder.go +++ b/materials/decoder.go @@ -13,7 +13,7 @@ import ( "github.com/hpinc/go3mf/spec" ) -func (Spec) DecodeAttribute(interface{}, spec.Attr) error { +func (Spec) NewAttr3MF(string) spec.Attr3MF { return nil } @@ -55,7 +55,7 @@ func (d *colorGroupDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *colorGroupDecoder) Start(attrs []spec.Attr) (errs error) { +func (d *colorGroupDecoder) Start(attrs []spec.XMLAttr) (errs error) { d.colorDecoder.resource = &d.resource for _, a := range attrs { if a.Name.Space == "" && a.Name.Local == attrID { @@ -75,7 +75,7 @@ type colorDecoder struct { resource *ColorGroup } -func (d *colorDecoder) Start(attrs []spec.Attr) error { +func (d *colorDecoder) Start(attrs []spec.XMLAttr) error { for _, a := range attrs { if a.Name.Space == "" && a.Name.Local == attrColor { c, err := spec.ParseRGBA(string(a.Value)) @@ -97,7 +97,7 @@ type tex2DCoordDecoder struct { resource *Texture2DGroup } -func (d *tex2DCoordDecoder) Start(attrs []spec.Attr) error { +func (d *tex2DCoordDecoder) Start(attrs []spec.XMLAttr) error { var ( text TextureCoord errs error @@ -146,7 +146,7 @@ func (d *tex2DGroupDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *tex2DGroupDecoder) Start(attrs []spec.Attr) error { +func (d *tex2DGroupDecoder) Start(attrs []spec.XMLAttr) error { var errs error d.tex2DCoordDecoder.resource = &d.resource for _, a := range attrs { @@ -184,7 +184,7 @@ func (d *texture2DDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *texture2DDecoder) Start(attrs []spec.Attr) error { +func (d *texture2DDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { if a.Name.Space != "" { @@ -237,7 +237,7 @@ func (d *compositeMaterialsDecoder) Child(name xml.Name) (child spec.ElementDeco return } -func (d *compositeMaterialsDecoder) Start(attrs []spec.Attr) error { +func (d *compositeMaterialsDecoder) Start(attrs []spec.XMLAttr) error { var errs error d.compositeDecoder.resource = &d.resource for _, a := range attrs { @@ -278,7 +278,7 @@ type compositeDecoder struct { resource *CompositeMaterials } -func (d *compositeDecoder) Start(attrs []spec.Attr) error { +func (d *compositeDecoder) Start(attrs []spec.XMLAttr) error { var ( composite Composite errs error @@ -323,7 +323,7 @@ func (d *multiPropertiesDecoder) Child(name xml.Name) (child spec.ElementDecoder return } -func (d *multiPropertiesDecoder) Start(attrs []spec.Attr) error { +func (d *multiPropertiesDecoder) Start(attrs []spec.XMLAttr) error { var errs error d.multiDecoder.resource = &d.resource for _, a := range attrs { @@ -363,7 +363,7 @@ type multiDecoder struct { resource *MultiProperties } -func (d *multiDecoder) Start(attrs []spec.Attr) error { +func (d *multiDecoder) Start(attrs []spec.XMLAttr) error { var ( multi Multi errs error diff --git a/materials/materials.go b/materials/materials.go index 247145a..e0ae3d3 100644 --- a/materials/materials.go +++ b/materials/materials.go @@ -8,6 +8,7 @@ import ( "image/color" "github.com/hpinc/go3mf" + "github.com/hpinc/go3mf/spec" ) const ( @@ -24,7 +25,7 @@ var DefaultExtension = go3mf.Extension{ } func init() { - go3mf.Register(Namespace, Spec{}) + spec.Register(Namespace, Spec{}) } type Spec struct{} diff --git a/materials/materials_test.go b/materials/materials_test.go index f9b7a74..29c033f 100644 --- a/materials/materials_test.go +++ b/materials/materials_test.go @@ -246,9 +246,6 @@ func Test_newBlendMethod(t *testing.T) { } func Test_newTextureFilter(t *testing.T) { - type args struct { - s string - } tests := []struct { name string want TextureFilter @@ -273,9 +270,6 @@ func Test_newTextureFilter(t *testing.T) { } func Test_newTileStyle(t *testing.T) { - type args struct { - s string - } tests := []struct { name string want TileStyle diff --git a/production/decoder.go b/production/decoder.go index 52dddc0..61c7c25 100644 --- a/production/decoder.go +++ b/production/decoder.go @@ -4,7 +4,6 @@ package production import ( - "github.com/hpinc/go3mf" specerr "github.com/hpinc/go3mf/errors" "github.com/hpinc/go3mf/spec" "github.com/hpinc/go3mf/uuid" @@ -14,58 +13,60 @@ func (Spec) CreateElementDecoder(_ interface{}, _ string) spec.ElementDecoder { return nil } -func (Spec) DecodeAttribute(parentNode interface{}, attr spec.Attr) (errs error) { - switch t := parentNode.(type) { - case *go3mf.Build: - if attr.Name.Local == attrProdUUID { - if err := uuid.Validate(string(attr.Value)); err != nil { - errs = specerr.Append(errs, specerr.NewParseAttrError(attr.Name.Local, true)) - } - t.AnyAttr = append(t.AnyAttr, &BuildAttr{UUID: string(attr.Value)}) +func (Spec) NewAttr3MF(parent string) spec.Attr3MF { + switch parent { + case "build": + return new(BuildAttr) + case "item": + return new(ItemAttr) + case "object": + return new(ObjectAttr) + case "component": + return new(ComponentAttr) + } + return nil +} + +func (u *BuildAttr) Unmarshal3MFAttr(a spec.XMLAttr) error { + if a.Name.Local == attrProdUUID { + if err := uuid.Validate(string(a.Value)); err != nil { + return specerr.NewParseAttrError(a.Name.Local, true) } - case *go3mf.Item: - switch attr.Name.Local { - case attrProdUUID: - if err := uuid.Validate(string(attr.Value)); err != nil { - errs = specerr.Append(errs, specerr.NewParseAttrError(attr.Name.Local, true)) - } - if ext := GetItemAttr(t); ext != nil { - ext.UUID = string(attr.Value) - } else { - t.AnyAttr = append(t.AnyAttr, &ItemAttr{UUID: string(attr.Value)}) - } - case attrPath: - if ext := GetItemAttr(t); ext != nil { - ext.Path = string(attr.Value) - } else { - t.AnyAttr = append(t.AnyAttr, &ItemAttr{Path: string(attr.Value)}) - } + u.UUID = string(a.Value) + } + return nil +} + +func (u *ItemAttr) Unmarshal3MFAttr(a spec.XMLAttr) error { + if a.Name.Local == attrProdUUID { + if err := uuid.Validate(string(a.Value)); err != nil { + return specerr.NewParseAttrError(a.Name.Local, true) } - case *go3mf.Object: - if attr.Name.Local == attrProdUUID { - if err := uuid.Validate(string(attr.Value)); err != nil { - errs = specerr.Append(errs, specerr.NewParseAttrError(attr.Name.Local, true)) - } - t.AnyAttr = append(t.AnyAttr, &ObjectAttr{UUID: string(attr.Value)}) + u.UUID = string(a.Value) + } else if a.Name.Local == attrPath { + u.Path = string(a.Value) + } + return nil +} + +func (u *ObjectAttr) Unmarshal3MFAttr(a spec.XMLAttr) error { + if a.Name.Local == attrProdUUID { + if err := uuid.Validate(string(a.Value)); err != nil { + return specerr.NewParseAttrError(a.Name.Local, true) } - case *go3mf.Component: - switch attr.Name.Local { - case attrProdUUID: - if err := uuid.Validate(string(attr.Value)); err != nil { - errs = specerr.Append(errs, specerr.NewParseAttrError(attr.Name.Local, true)) - } - if ext := GetComponentAttr(t); ext != nil { - ext.UUID = string(attr.Value) - } else { - t.AnyAttr = append(t.AnyAttr, &ComponentAttr{UUID: string(attr.Value)}) - } - case attrPath: - if ext := GetComponentAttr(t); ext != nil { - ext.Path = string(attr.Value) - } else { - t.AnyAttr = append(t.AnyAttr, &ComponentAttr{Path: string(attr.Value)}) - } + u.UUID = string(a.Value) + } + return nil +} + +func (u *ComponentAttr) Unmarshal3MFAttr(a spec.XMLAttr) error { + if a.Name.Local == attrProdUUID { + if err := uuid.Validate(string(a.Value)); err != nil { + return specerr.NewParseAttrError(a.Name.Local, true) } + u.UUID = string(a.Value) + } else if a.Name.Local == attrPath { + u.Path = string(a.Value) } - return + return nil } diff --git a/production/decoder_test.go b/production/decoder_test.go index c2ab34c..80b4495 100644 --- a/production/decoder_test.go +++ b/production/decoder_test.go @@ -10,14 +10,15 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf" "github.com/hpinc/go3mf/errors" + "github.com/hpinc/go3mf/spec" ) func TestDecode(t *testing.T) { components := &go3mf.Object{ - AnyAttr: go3mf.AnyAttr{&ObjectAttr{UUID: "cb828680-8895-4e08-a1fc-be63e033df15"}}, + AnyAttr: spec.AnyAttr{&ObjectAttr{UUID: "cb828680-8895-4e08-a1fc-be63e033df15"}}, ID: 20, Components: &go3mf.Components{Component: []*go3mf.Component{{ - AnyAttr: go3mf.AnyAttr{&ComponentAttr{ + AnyAttr: spec.AnyAttr{&ComponentAttr{ Path: "/3D/other.model", UUID: "cb828680-8895-4e08-a1fc-be63e033df16", }}, @@ -28,13 +29,13 @@ func TestDecode(t *testing.T) { want := &go3mf.Model{Path: "/3D/3dmodel.model", Resources: go3mf.Resources{ Objects: []*go3mf.Object{components}, }, Build: go3mf.Build{ - AnyAttr: go3mf.AnyAttr{&BuildAttr{UUID: "e9e25302-6428-402e-8633-cc95528d0ed3"}}}, + AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "e9e25302-6428-402e-8633-cc95528d0ed3"}}}, } want.Build.Items = append(want.Build.Items, &go3mf.Item{ObjectID: 20, - AnyAttr: go3mf.AnyAttr{&ItemAttr{UUID: "e9e25302-6428-402e-8633-cc95528d0ed2"}}, + AnyAttr: spec.AnyAttr{&ItemAttr{UUID: "e9e25302-6428-402e-8633-cc95528d0ed2"}}, Transform: go3mf.Matrix{1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, -66.4, -87.1, 8.8, 1}, }, &go3mf.Item{ObjectID: 8, - AnyAttr: go3mf.AnyAttr{&ItemAttr{ + AnyAttr: spec.AnyAttr{&ItemAttr{ Path: "/3D/other.model", UUID: "e9e25302-6428-402e-8633-cc95528d0ed4", }}, diff --git a/production/encoder.go b/production/encoder.go index a6b5a3b..71b0c9e 100644 --- a/production/encoder.go +++ b/production/encoder.go @@ -10,41 +10,31 @@ import ( ) // Marshal3MFAttr encodes the resource attributes. -func (u *BuildAttr) Marshal3MFAttr(_ spec.Encoder) ([]xml.Attr, error) { - return []xml.Attr{ - {Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: u.UUID}, - }, nil +func (u *BuildAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: u.UUID}) + return nil } // Marshal3MFAttr encodes the resource attributes. -func (u *ObjectAttr) Marshal3MFAttr(_ spec.Encoder) ([]xml.Attr, error) { - return []xml.Attr{ - {Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: u.UUID}, - }, nil +func (u *ObjectAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: u.UUID}) + return nil } // Marshal3MFAttr encodes the resource attributes. -func (p *ItemAttr) Marshal3MFAttr(_ spec.Encoder) ([]xml.Attr, error) { - if p.Path == "" { - return []xml.Attr{ - {Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: p.UUID}, - }, nil +func (u *ItemAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { + if u.Path != "" { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrPath}, Value: u.Path}) } - return []xml.Attr{ - {Name: xml.Name{Space: Namespace, Local: attrPath}, Value: p.Path}, - {Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: p.UUID}, - }, nil + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: u.UUID}) + return nil } // Marshal3MFAttr encodes the resource attributes. -func (p *ComponentAttr) Marshal3MFAttr(_ spec.Encoder) ([]xml.Attr, error) { - if p.Path == "" { - return []xml.Attr{ - {Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: p.UUID}, - }, nil +func (u *ComponentAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { + if u.Path != "" { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrPath}, Value: u.Path}) } - return []xml.Attr{ - {Name: xml.Name{Space: Namespace, Local: attrPath}, Value: p.Path}, - {Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: p.UUID}, - }, nil + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: u.UUID}) + return nil } diff --git a/production/encoder_test.go b/production/encoder_test.go index f6fa973..8e1f441 100644 --- a/production/encoder_test.go +++ b/production/encoder_test.go @@ -8,28 +8,29 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf" + "github.com/hpinc/go3mf/spec" ) func TestMarshalModel(t *testing.T) { components := &go3mf.Object{ - AnyAttr: go3mf.AnyAttr{&ObjectAttr{UUID: "cb828680-8895-4e08-a1fc-be63e033df15"}}, + AnyAttr: spec.AnyAttr{&ObjectAttr{UUID: "cb828680-8895-4e08-a1fc-be63e033df15"}}, ID: 20, Components: &go3mf.Components{Component: []*go3mf.Component{{ ObjectID: 8, - AnyAttr: go3mf.AnyAttr{&ComponentAttr{ + AnyAttr: spec.AnyAttr{&ComponentAttr{ Path: "/3D/other.model", UUID: "cb828680-8895-4e08-a1fc-be63e033df16", }}}, }}, } m := &go3mf.Model{Path: "/3D/3dmodel.model", Build: go3mf.Build{ - AnyAttr: go3mf.AnyAttr{&BuildAttr{UUID: "e9e25302-6428-402e-8633-cc95528d0ed3"}}, + AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "e9e25302-6428-402e-8633-cc95528d0ed3"}}, }} m.Resources = go3mf.Resources{Objects: []*go3mf.Object{components}} m.Build.Items = append(m.Build.Items, &go3mf.Item{ObjectID: 20, - AnyAttr: go3mf.AnyAttr{&ItemAttr{UUID: "e9e25302-6428-402e-8633-cc95528d0ed2"}}, + AnyAttr: spec.AnyAttr{&ItemAttr{UUID: "e9e25302-6428-402e-8633-cc95528d0ed2"}}, }, &go3mf.Item{ObjectID: 8, - AnyAttr: go3mf.AnyAttr{&ItemAttr{ + AnyAttr: spec.AnyAttr{&ItemAttr{ Path: "/3D/other.model", UUID: "e9e25302-6428-402e-8633-cc95528d0ed4", }}, diff --git a/production/production.go b/production/production.go index 083b626..ce4dce7 100644 --- a/production/production.go +++ b/production/production.go @@ -7,6 +7,7 @@ import ( "errors" "github.com/hpinc/go3mf" + "github.com/hpinc/go3mf/spec" "github.com/hpinc/go3mf/uuid" ) @@ -32,7 +33,7 @@ const ( type Spec struct{} func init() { - go3mf.Register(Namespace, Spec{}) + spec.Register(Namespace, Spec{}) } // BuildAttr provides a UUID in the root model file build element to ensure @@ -41,6 +42,8 @@ type BuildAttr struct { UUID string } +func (BuildAttr) Namespace() string { return Namespace } + func GetBuildAttr(build *go3mf.Build) *BuildAttr { for _, a := range build.AnyAttr { if a, ok := a.(*BuildAttr); ok { @@ -56,6 +59,8 @@ type ObjectAttr struct { UUID string } +func (ObjectAttr) Namespace() string { return Namespace } + func GetObjectAttr(obj *go3mf.Object) *ObjectAttr { for _, a := range obj.AnyAttr { if a, ok := a.(*ObjectAttr); ok { @@ -72,6 +77,8 @@ type ItemAttr struct { Path string } +func (ItemAttr) Namespace() string { return Namespace } + func GetItemAttr(item *go3mf.Item) *ItemAttr { for _, a := range item.AnyAttr { if a, ok := a.(*ItemAttr); ok { @@ -97,6 +104,8 @@ type ComponentAttr struct { Path string } +func (ComponentAttr) Namespace() string { return Namespace } + func GetComponentAttr(comp *go3mf.Component) *ComponentAttr { for _, a := range comp.AnyAttr { if a, ok := a.(*ComponentAttr); ok { @@ -152,5 +161,4 @@ func SetMissingUUIDs(m *go3mf.Model) { } return nil }) - return } diff --git a/production/validate_test.go b/production/validate_test.go index 126add0..209346d 100644 --- a/production/validate_test.go +++ b/production/validate_test.go @@ -10,6 +10,7 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf" "github.com/hpinc/go3mf/errors" + "github.com/hpinc/go3mf/spec" ) func TestValidate(t *testing.T) { @@ -25,31 +26,31 @@ func TestValidate(t *testing.T) { fmt.Sprintf("Build: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"buildEmptyUUID", &go3mf.Model{Build: go3mf.Build{ - AnyAttr: go3mf.AnyAttr{&BuildAttr{}}}}, []string{ + AnyAttr: spec.AnyAttr{&BuildAttr{}}}}, []string{ fmt.Sprintf("Build: %v", ErrUUID), }}, {"buildNonValidUUID", &go3mf.Model{Build: go3mf.Build{ - AnyAttr: go3mf.AnyAttr{&BuildAttr{"a-b-c-d"}}}}, []string{ + AnyAttr: spec.AnyAttr{&BuildAttr{"a-b-c-d"}}}}, []string{ fmt.Sprintf("Build: %v", ErrUUID), }}, {"extReq", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, Resources: go3mf.Resources{Objects: []*go3mf.Object{ - {ID: 5, AnyAttr: go3mf.AnyAttr{&ObjectAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d481"}}, Components: &go3mf.Components{Component: []*go3mf.Component{ - {ObjectID: 1, AnyAttr: go3mf.AnyAttr{ + {ID: 5, AnyAttr: spec.AnyAttr{&ObjectAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d481"}}, Components: &go3mf.Components{Component: []*go3mf.Component{ + {ObjectID: 1, AnyAttr: spec.AnyAttr{ &ComponentAttr{Path: "/other.model", UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d480"}, }}}}}}}, Build: go3mf.Build{ - AnyAttr: go3mf.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ - {ObjectID: 1, AnyAttr: go3mf.AnyAttr{&ItemAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d478", Path: "/other.model"}}}, + AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ + {ObjectID: 1, AnyAttr: spec.AnyAttr{&ItemAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d478", Path: "/other.model"}}}, }}}, []string{ fmt.Sprintf("/other.model@Resources@Object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"items", &go3mf.Model{Build: go3mf.Build{ - AnyAttr: go3mf.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ - {ObjectID: 1, AnyAttr: go3mf.AnyAttr{&ItemAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d478", Path: "/other.model"}}}, + AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ + {ObjectID: 1, AnyAttr: spec.AnyAttr{&ItemAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d478", Path: "/other.model"}}}, {ObjectID: 1}, - {ObjectID: 1, AnyAttr: go3mf.AnyAttr{&ItemAttr{}}}, - {ObjectID: 1, AnyAttr: go3mf.AnyAttr{&ItemAttr{UUID: "a-b-c-d"}}}, + {ObjectID: 1, AnyAttr: spec.AnyAttr{&ItemAttr{}}}, + {ObjectID: 1, AnyAttr: spec.AnyAttr{&ItemAttr{UUID: "a-b-c-d"}}}, }}, Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, Resources: go3mf.Resources{Objects: []*go3mf.Object{{ID: 1, Mesh: validMesh.Mesh}}}}, []string{ @@ -61,25 +62,25 @@ func TestValidate(t *testing.T) { }}, {"components", &go3mf.Model{Resources: go3mf.Resources{ Objects: []*go3mf.Object{ - {ID: 2, Mesh: validMesh.Mesh, AnyAttr: go3mf.AnyAttr{&ObjectAttr{UUID: "a-b-c-d"}}}, - {ID: 3, AnyAttr: go3mf.AnyAttr{&ObjectAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d483"}}, Components: &go3mf.Components{Component: []*go3mf.Component{ - {ObjectID: 2, AnyAttr: go3mf.AnyAttr{&ComponentAttr{}}}, - {ObjectID: 2, AnyAttr: go3mf.AnyAttr{&ComponentAttr{UUID: "a-b-c-d"}}}, + {ID: 2, Mesh: validMesh.Mesh, AnyAttr: spec.AnyAttr{&ObjectAttr{UUID: "a-b-c-d"}}}, + {ID: 3, AnyAttr: spec.AnyAttr{&ObjectAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d483"}}, Components: &go3mf.Components{Component: []*go3mf.Component{ + {ObjectID: 2, AnyAttr: spec.AnyAttr{&ComponentAttr{}}}, + {ObjectID: 2, AnyAttr: spec.AnyAttr{&ComponentAttr{UUID: "a-b-c-d"}}}, {ObjectID: 2}, }}}, }, - }, Build: go3mf.Build{AnyAttr: go3mf.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}}, []string{ + }, Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}}, []string{ fmt.Sprintf("Resources@Object#0: %v", ErrUUID), fmt.Sprintf("Resources@Object#1@Components@Component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), fmt.Sprintf("Resources@Object#1@Components@Component#1: %v", ErrUUID), fmt.Sprintf("Resources@Object#1@Components@Component#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, - {"child", &go3mf.Model{Build: go3mf.Build{AnyAttr: go3mf.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}, + {"child", &go3mf.Model{Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}, Childs: map[string]*go3mf.ChildModel{ "/b.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}, "/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Components: &go3mf.Components{Component: []*go3mf.Component{ - {ObjectID: 1, AnyAttr: go3mf.AnyAttr{&ComponentAttr{Path: "/b.model"}}}, + {ObjectID: 1, AnyAttr: spec.AnyAttr{&ComponentAttr{Path: "/b.model"}}}, }}}, }}}}}, []string{ fmt.Sprintf("/b.model@Resources@Object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), diff --git a/read.go b/read.go index 6c4245e..e0b3245 100644 --- a/read.go +++ b/read.go @@ -80,7 +80,7 @@ func decodeModelFile(ctx context.Context, r io.Reader, model *Model, path string names = append(names, currentName) currentName = tp.Name currentDecoder = tmpDecoder - err := currentDecoder.Start(*(*[]spec.Attr)(unsafe.Pointer(&tp.Attr))) + err := currentDecoder.Start(*(*[]spec.XMLAttr)(unsafe.Pointer(&tp.Attr))) if err != nil { for i := len(state) - 1; i >= 0; i-- { if ew, ok := state[i].(spec.ErrorWrapper); ok { diff --git a/read_test.go b/read_test.go index 1ff7870..48ff86d 100644 --- a/read_test.go +++ b/read_test.go @@ -25,7 +25,8 @@ import ( const fakeExtension = "http://dummy.com/fake_ext" -var fooName = xml.Name{Space: "http://dummy.com/foo", Local: "fooname"} +var fooSpace = "http://dummy.com/foo" +var fooName = xml.Name{Space: fooSpace, Local: "fooname"} var fakeSpec = Extension{ Namespace: fakeExtension, @@ -34,13 +35,17 @@ var fakeSpec = Extension{ } var fooSpec = Extension{ - Namespace: fooName.Space, + Namespace: fooSpace, LocalName: "foo", IsRequired: false, } type qmExtension struct{} +func (qmExtension) NewAttr3MF(parent string) spec.Attr3MF { + return &fakeAttr{} +} + func (qmExtension) CreateElementDecoder(parent interface{}, name string) spec.ElementDecoder { if e, ok := parent.(*Resources); ok { return &fakeAssetDecoder{resources: e} @@ -48,22 +53,6 @@ func (qmExtension) CreateElementDecoder(parent interface{}, name string) spec.El return nil } -func (qmExtension) DecodeAttribute(parentNode interface{}, attr spec.Attr) error { - switch t := parentNode.(type) { - case *Object: - t.AnyAttr = append(t.AnyAttr, &fakeAttr{string(attr.Value)}) - case *Build: - t.AnyAttr = append(t.AnyAttr, &fakeAttr{string(attr.Value)}) - case *Model: - t.AnyAttr = append(t.AnyAttr, &fakeAttr{string(attr.Value)}) - case *Item: - t.AnyAttr = append(t.AnyAttr, &fakeAttr{string(attr.Value)}) - case *Component: - t.AnyAttr = append(t.AnyAttr, &fakeAttr{string(attr.Value)}) - } - return nil -} - func (qmExtension) Validate(model interface{}, path string, element interface{}) error { if _, ok := element.(*Model); !ok { return nil @@ -92,12 +81,24 @@ type fakeAttr struct { func (f *fakeAttr) ObjectPath() string { return f.Value } +func (f fakeAttr) Namespace() string { return fakeExtension } + +func (f fakeAttr) Marshal3MFAttr(enc spec.Encoder, start *xml.StartElement) error { + start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: fakeExtension, Local: "value"}, Value: f.Value}) + return nil +} + +func (f *fakeAttr) Unmarshal3MFAttr(a spec.XMLAttr) error { + f.Value = string(a.Value) + return nil +} + type fakeAssetDecoder struct { baseDecoder resources *Resources } -func (f *fakeAssetDecoder) Start(att []spec.Attr) error { +func (f *fakeAssetDecoder) Start(att []spec.XMLAttr) error { id, _ := strconv.ParseUint(string(att[0].Value), 10, 32) f.resources.Assets = append(f.resources.Assets, &fakeAsset{ID: uint32(id)}) return nil @@ -316,15 +317,15 @@ func TestDecoder_processRootModel_Fail(t *testing.T) { } func TestDecoder_processRootModel(t *testing.T) { - Register(fakeSpec.Namespace, new(qmExtension)) + spec.Register(fakeSpec.Namespace, new(qmExtension)) baseMaterials := &BaseMaterials{ID: 5, Materials: []Base{ {Name: "Blue PLA", Color: color.RGBA{0, 0, 255, 255}}, - {Name: "Red ABS", Color: color.RGBA{255, 0, 0, 255}, AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval8"}}}}, - }, AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval7"}}}} + {Name: "Red ABS", Color: color.RGBA{255, 0, 0, 255}, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval8"}}}}}, + }, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval7"}}}}} meshRes := &Object{ ID: 8, Name: "Box 1", Thumbnail: "/a.png", PID: 5, PartNumber: "11111111-1111-1111-1111-111111111111", Mesh: &Mesh{ - AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval9"}}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval9"}}}}, Any: Any{spec.UnknownTokens{ xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "fake"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "fake"}}, @@ -342,7 +343,7 @@ func TestDecoder_processRootModel(t *testing.T) { {0, 100, 100}, }...) meshRes.Mesh.Triangles = append(meshRes.Mesh.Triangles, []Triangle{ - {V1: 3, V2: 2, V3: 1, PID: 5, P1: 0, P2: 0, P3: 0}, + {V1: 3, V2: 2, V3: 1, PID: 5, P1: 0, P2: 0, P3: 0, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "t1"}}}}}, {V1: 1, V2: 0, V3: 3, PID: 5, P1: 0, P2: 0, P3: 0}, {V1: 4, V2: 5, V3: 6, PID: 5, P1: 1, P2: 1, P3: 1}, {V1: 6, V2: 7, V3: 4, PID: 5, P1: 1, P2: 1, P3: 1}, @@ -358,14 +359,14 @@ func TestDecoder_processRootModel(t *testing.T) { components := &Object{ ID: 20, Type: ObjectTypeSupport, - AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval6"}}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval6"}}}}, Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}, {Name: xml.Name{Space: "qm", Local: "CustomMetadata4"}, Type: "xs:boolean", Value: "2"}}, Components: &Components{ - AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval4"}}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval4"}}}}, Component: []*Component{ { ObjectID: 8, Transform: Matrix{3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, -66.4, -87.1, 8.8, 1}, - AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval5"}}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval5"}}}}, }, }, }, @@ -393,12 +394,12 @@ func TestDecoder_processRootModel(t *testing.T) { xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "resources"}}, }, }}, Objects: []*Object{meshRes, components}, - AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval3"}}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval3"}}}}, }, Build: Build{ - AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval1"}}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval1"}}}}, }, - AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval"}}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval"}}}}, Any: Any{ spec.UnknownTokens{ xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "other"}}, @@ -417,7 +418,7 @@ func TestDecoder_processRootModel(t *testing.T) { want.Build.Items = append(want.Build.Items, &Item{ ObjectID: 20, PartNumber: "bob", Transform: Matrix{1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, -66.4, -87.1, 8.8, 1}, Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}}, - AnyAttr: AnyAttr{&spec.UnknownAttrs{{Name: fooName, Value: "fooval2"}}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval2"}}}}, }) want.Metadata = append(want.Metadata, []Metadata{ {Name: xml.Name{Local: "Application"}, Value: "go3mf app"}, @@ -444,7 +445,7 @@ func TestDecoder_processRootModel(t *testing.T) { - + @@ -632,7 +633,7 @@ func TestNewDecoder(t *testing.T) { } func TestDecoder_processRootModel_warns(t *testing.T) { - Register(fakeSpec.Namespace, new(qmExtension)) + spec.Register(fakeSpec.Namespace, new(qmExtension)) want := []string{ fmt.Sprintf("Resources@BaseMaterials#0@Base#0: %v", specerr.NewParseAttrError("displaycolor", true)), fmt.Sprintf("Resources@BaseMaterials#1: %v", specerr.NewParseAttrError("id", true)), diff --git a/slices/decoder.go b/slices/decoder.go index 7815428..0e15b89 100644 --- a/slices/decoder.go +++ b/slices/decoder.go @@ -19,38 +19,30 @@ func (Spec) CreateElementDecoder(parent interface{}, name string) spec.ElementDe return nil } -func (Spec) DecodeAttribute(parentNode interface{}, attr spec.Attr) error { - if parentNode, ok := parentNode.(*go3mf.Object); ok { - return objectAttrDecoder(parentNode, attr) +func (Spec) NewAttr3MF(parentNode string) spec.Attr3MF { + switch parentNode { + case "object": + return new(ObjectAttr) } return nil } -// objectAttrDecoder decodes the slice attributes of an ObjectReosurce. -func objectAttrDecoder(o *go3mf.Object, a spec.Attr) (errs error) { +func (u *ObjectAttr) Unmarshal3MFAttr(a spec.XMLAttr) error { switch a.Name.Local { case attrSliceRefID: val, err := strconv.ParseUint(string(a.Value), 10, 32) if err != nil { - errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, true)) - } - if ext := GetObjectAttr(o); ext != nil { - ext.SliceStackID = uint32(val) - } else { - o.AnyAttr = append(o.AnyAttr, &ObjectAttr{SliceStackID: uint32(val)}) + return specerr.NewParseAttrError(a.Name.Local, true) } + u.SliceStackID = uint32(val) case attrMeshRes: res, ok := newMeshResolution(string(a.Value)) if !ok { - errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, false)) - } - if ext := GetObjectAttr(o); ext != nil { - ext.MeshResolution = res - } else { - o.AnyAttr = append(o.AnyAttr, &ObjectAttr{MeshResolution: res}) + return specerr.NewParseAttrError(a.Name.Local, false) } + u.MeshResolution = res } - return + return nil } type sliceStackDecoder struct { @@ -78,7 +70,7 @@ func (d *sliceStackDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *sliceStackDecoder) Start(attrs []spec.Attr) error { +func (d *sliceStackDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { switch a.Name.Local { @@ -107,7 +99,7 @@ type sliceRefDecoder struct { resource *SliceStack } -func (d *sliceRefDecoder) Start(attrs []spec.Attr) error { +func (d *sliceRefDecoder) Start(attrs []spec.XMLAttr) error { var ( sliceStackID uint32 path string @@ -160,7 +152,7 @@ func (d *sliceDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *sliceDecoder) Start(attrs []spec.Attr) error { +func (d *sliceDecoder) Start(attrs []spec.XMLAttr) error { d.polygonDecoder.slice = &d.slice d.polygonVerticesDecoder.slice = &d.slice var errs error @@ -186,7 +178,7 @@ type polygonVerticesDecoder struct { polygonVertexDecoder polygonVertexDecoder } -func (d *polygonVerticesDecoder) Start(_ []spec.Attr) error { +func (d *polygonVerticesDecoder) Start(_ []spec.XMLAttr) error { d.polygonVertexDecoder.slice = d.slice return nil } @@ -203,7 +195,7 @@ type polygonVertexDecoder struct { slice *Slice } -func (d *polygonVertexDecoder) Start(attrs []spec.Attr) error { +func (d *polygonVertexDecoder) Start(attrs []spec.XMLAttr) error { var ( p go3mf.Point2D errs error @@ -245,7 +237,7 @@ func (d *polygonDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *polygonDecoder) Start(attrs []spec.Attr) error { +func (d *polygonDecoder) Start(attrs []spec.XMLAttr) error { var errs error polygonIndex := len(d.slice.Polygons) d.slice.Polygons = append(d.slice.Polygons, Polygon{}) @@ -271,7 +263,7 @@ type polygonSegmentDecoder struct { polygon *Polygon } -func (d *polygonSegmentDecoder) Start(attrs []spec.Attr) error { +func (d *polygonSegmentDecoder) Start(attrs []spec.XMLAttr) error { var ( segment Segment hasP1, hasP2 bool diff --git a/slices/decoder_test.go b/slices/decoder_test.go index 7b42bf5..8a3f865 100644 --- a/slices/decoder_test.go +++ b/slices/decoder_test.go @@ -10,6 +10,7 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf" specerr "github.com/hpinc/go3mf/errors" + "github.com/hpinc/go3mf/spec" ) func TestDecode(t *testing.T) { @@ -31,7 +32,7 @@ func TestDecode(t *testing.T) { meshRes := &go3mf.Object{ Mesh: new(go3mf.Mesh), ID: 8, Name: "Box 1", - AnyAttr: go3mf.AnyAttr{&ObjectAttr{SliceStackID: 3, MeshResolution: ResolutionLow}}, + AnyAttr: spec.AnyAttr{&ObjectAttr{SliceStackID: 3, MeshResolution: ResolutionLow}}, } want := &go3mf.Model{ diff --git a/slices/encoder.go b/slices/encoder.go index fc79742..d186ebc 100644 --- a/slices/encoder.go +++ b/slices/encoder.go @@ -12,11 +12,12 @@ import ( ) // Marshal3MFAttr encodes the resource attributes. -func (s *ObjectAttr) Marshal3MFAttr(_ spec.Encoder) ([]xml.Attr, error) { - return []xml.Attr{ - {Name: xml.Name{Space: Namespace, Local: attrSliceRefID}, Value: strconv.FormatUint(uint64(s.SliceStackID), 10)}, - {Name: xml.Name{Space: Namespace, Local: attrMeshRes}, Value: s.MeshResolution.String()}, - }, nil +func (s *ObjectAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { + start.Attr = append(start.Attr, + xml.Attr{Name: xml.Name{Space: Namespace, Local: attrSliceRefID}, Value: strconv.FormatUint(uint64(s.SliceStackID), 10)}, + xml.Attr{Name: xml.Name{Space: Namespace, Local: attrMeshRes}, Value: s.MeshResolution.String()}, + ) + return nil } // Marshal3MF encodes the resource. diff --git a/slices/encoder_test.go b/slices/encoder_test.go index dae01fc..d7aabcd 100644 --- a/slices/encoder_test.go +++ b/slices/encoder_test.go @@ -9,6 +9,7 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf" + "github.com/hpinc/go3mf/spec" ) func TestMarshalModel(t *testing.T) { @@ -30,7 +31,7 @@ func TestMarshalModel(t *testing.T) { meshRes := &go3mf.Object{ Mesh: new(go3mf.Mesh), ID: 8, Name: "Box 1", - AnyAttr: go3mf.AnyAttr{&ObjectAttr{SliceStackID: 3, MeshResolution: ResolutionLow}}, + AnyAttr: spec.AnyAttr{&ObjectAttr{SliceStackID: 3, MeshResolution: ResolutionLow}}, } baseMaterial := &go3mf.BaseMaterials{ID: 10, Materials: []go3mf.Base{{Name: "a", Color: color.RGBA{R: 1}}, {Name: "b", Color: color.RGBA{R: 1}}}} diff --git a/slices/slices.go b/slices/slices.go index 092a156..53fe980 100644 --- a/slices/slices.go +++ b/slices/slices.go @@ -5,7 +5,9 @@ package slices import ( "errors" + "github.com/hpinc/go3mf" + "github.com/hpinc/go3mf/spec" ) // Namespace is the canonical name of this extension. @@ -18,7 +20,7 @@ var DefaultExtension = go3mf.Extension{ } func init() { - go3mf.Register(Namespace, Spec{}) + spec.Register(Namespace, Spec{}) } type Spec struct{} @@ -122,6 +124,8 @@ type ObjectAttr struct { MeshResolution MeshResolution } +func (ObjectAttr) Namespace() string { return Namespace } + const ( attrSliceStack = "slicestack" attrID = "id" diff --git a/slices/validate_test.go b/slices/validate_test.go index b6b9b8c..ad57452 100644 --- a/slices/validate_test.go +++ b/slices/validate_test.go @@ -11,6 +11,7 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf" "github.com/hpinc/go3mf/errors" + "github.com/hpinc/go3mf/spec" ) func TestValidate(t *testing.T) { @@ -21,13 +22,13 @@ func TestValidate(t *testing.T) { want []string }{ {"extRequired", &go3mf.Model{ - AnyAttr: go3mf.AnyAttr{&ObjectAttr{SliceStackID: 10}}, + AnyAttr: spec.AnyAttr{&ObjectAttr{SliceStackID: 10}}, Extensions: []go3mf.Extension{{Namespace: Namespace, LocalName: "s", IsRequired: true}}, Resources: go3mf.Resources{ Assets: []go3mf.Asset{ &SliceStack{ID: 1, Slices: []*Slice{{TopZ: 1}}}, }, Objects: []*go3mf.Object{ - {ID: 2, AnyAttr: go3mf.AnyAttr{&ObjectAttr{ + {ID: 2, AnyAttr: spec.AnyAttr{&ObjectAttr{ SliceStackID: 1, MeshResolution: ResolutionLow, }}}, }}, @@ -125,30 +126,30 @@ func TestValidate(t *testing.T) { }}}}, }, Objects: []*go3mf.Object{ - {ID: 1, Mesh: &go3mf.Mesh{}, AnyAttr: go3mf.AnyAttr{&ObjectAttr{ + {ID: 1, Mesh: &go3mf.Mesh{}, AnyAttr: spec.AnyAttr{&ObjectAttr{ SliceStackID: 1, }}}, - {ID: 2, Type: go3mf.ObjectTypeSupport, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 1}}}, AnyAttr: go3mf.AnyAttr{&ObjectAttr{ + {ID: 2, Type: go3mf.ObjectTypeSupport, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 1}}}, AnyAttr: spec.AnyAttr{&ObjectAttr{ SliceStackID: 3, MeshResolution: ResolutionLow, }}}, {ID: 4, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 10, Transform: go3mf.Matrix{2, 3, 0, 0, 1, 3, 0, 0, 0, 0, 2, 0, 2, 3, 4, 1}}}}, - AnyAttr: go3mf.AnyAttr{&ObjectAttr{ + AnyAttr: spec.AnyAttr{&ObjectAttr{ SliceStackID: 0, }}}, - {ID: 5, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 12}}}, AnyAttr: go3mf.AnyAttr{&ObjectAttr{ + {ID: 5, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 12}}}, AnyAttr: spec.AnyAttr{&ObjectAttr{ SliceStackID: 6, }}}, - {ID: 7, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 1}, {ObjectID: 4}}}, AnyAttr: go3mf.AnyAttr{&ObjectAttr{ + {ID: 7, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 1}, {ObjectID: 4}}}, AnyAttr: spec.AnyAttr{&ObjectAttr{ SliceStackID: 9, }}}, - {ID: 10, Type: go3mf.ObjectTypeSolidSupport, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 1}}}, AnyAttr: go3mf.AnyAttr{&ObjectAttr{ + {ID: 10, Type: go3mf.ObjectTypeSolidSupport, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 1}}}, AnyAttr: spec.AnyAttr{&ObjectAttr{ SliceStackID: 3, }}}, {ID: 12, Components: &go3mf.Components{Component: []*go3mf.Component{ {ObjectID: 7, Transform: go3mf.Matrix{2, 3, 0, 0, 1, 3, 1, 0, 0, 0, 1, 0, 2, 3, 4, 1}}, {ObjectID: 12}, {ObjectID: 5}, - }}, AnyAttr: go3mf.AnyAttr{&ObjectAttr{ + }}, AnyAttr: spec.AnyAttr{&ObjectAttr{ SliceStackID: 11, }}}, }}}, []string{ diff --git a/spec.go b/spec.go index b699908..151303a 100644 --- a/spec.go +++ b/spec.go @@ -4,8 +4,6 @@ package go3mf import ( - "sync" - "github.com/hpinc/go3mf/spec" ) @@ -13,38 +11,6 @@ type objectPather interface { ObjectPath() string } -var ( - specMu sync.RWMutex - specs = make(map[string]spec.Spec) -) - -// Register makes a spec available by the provided namesoace. -// If Register is called twice with the same name or if spec is nil, -// it panics. -func Register(namespace string, spec spec.Spec) { - specMu.Lock() - defer specMu.Unlock() - specs[namespace] = spec -} - -func loadExtension(ns string) (spec.Spec, bool) { - specMu.RLock() - ext, ok := specs[ns] - specMu.RUnlock() - return ext, ok -} - -func loadValidator(ns string) (spec.ValidateSpec, bool) { - specMu.RLock() - ext, ok := specs[ns] - specMu.RUnlock() - if ok { - ext, ok := ext.(spec.ValidateSpec) - return ext, ok - } - return nil, false -} - // UnknownAsset wraps a spec.UnknownTokens to fulfill // the Asset interface. type UnknownAsset struct { diff --git a/spec/spec.go b/spec/spec.go index 0166388..8be0c1a 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -5,13 +5,14 @@ package spec import ( "encoding/xml" + "sync" ) // Spec is the interface that must be implemented by a 3mf spec. // // Specs may implement ValidateSpec. type Spec interface { - DecodeAttribute(parent interface{}, attr Attr) error + NewAttr3MF(parent string) Attr3MF CreateElementDecoder(parent interface{}, name string) ElementDecoder } @@ -29,8 +30,8 @@ type ValidateSpec interface { Validate(model interface{}, path string, element interface{}) error } -// An Attr represents an attribute in an XML element (Name=Value). -type Attr struct { +// An XMLAttr represents an attribute in an XML element (Name=Value). +type XMLAttr struct { Name xml.Name Value []byte } @@ -47,10 +48,16 @@ type Marshaler interface { Marshal3MF(Encoder) error } +// UnmarshalerAttr is the interface implemented by objects that can unmarshal +// an XML element description of themselves. +type UnmarshalerAttr interface { + Unmarshal3MFAttr(XMLAttr) error +} + // MarshalerAttr is the interface implemented by objects that can marshal // themselves into valid XML attributes. type MarshalerAttr interface { - Marshal3MFAttr(Encoder) ([]xml.Attr, error) + Marshal3MFAttr(Encoder, *xml.StartElement) error } type ErrorWrapper interface { @@ -59,7 +66,7 @@ type ErrorWrapper interface { // ElementDecoder defines the minimum contract to decode a 3MF node. type ElementDecoder interface { - Start([]Attr) error + Start([]XMLAttr) error End() } @@ -99,56 +106,70 @@ type Encoder interface { SetSkipAttrEscape(bool) } -// An UnknownAttrs represents a list of attributes -// that are not supported by any loaded Spec. -type UnknownAttrs []xml.Attr +var ( + specMu sync.RWMutex + specs = make(map[string]Spec) +) -func (u UnknownAttrs) Marshal3MFAttr(enc Encoder) ([]xml.Attr, error) { - return u, nil +// Register makes a spec available by the provided namesoace. +// If Register is called twice with the same name or if spec is nil, +// it panics. +func Register(namespace string, spec Spec) { + specMu.Lock() + defer specMu.Unlock() + specs[namespace] = spec } -// UnknownTokens represents a section of an xml -// that cannot be decoded by any loaded Spec. -type UnknownTokens []xml.Token - -func (u UnknownTokens) Marshal3MF(enc Encoder) error { - for _, t := range u { - enc.EncodeToken(t) - } - return nil +type Attr3MF interface { + UnmarshalerAttr + MarshalerAttr + Namespace() string } -// UnknownTokensDecoder can be used by spec decoders to maintain the -// xml tree elements of unknown extensions. -type UnknownTokensDecoder struct { - Name xml.Name +type AnyAttr []Attr3MF - tokens UnknownTokens +func (a AnyAttr) Get(namespace string) Attr3MF { + for _, v := range a { + if v.Namespace() == namespace { + return v + } + } + return nil } -func (d *UnknownTokensDecoder) Start(attrs []Attr) error { - var xattrs []xml.Attr - if len(attrs) > 0 { - xattrs = make([]xml.Attr, len(attrs)) - for i, att := range attrs { - xattrs[i] = xml.Attr{Name: att.Name, Value: string(att.Value)} +func (a AnyAttr) Marshal3MFAttr(x Encoder, start *xml.StartElement) error { + for _, ext := range a { + err := ext.Marshal3MFAttr(x, start) + if err != nil { + return err } } - d.AppendToken(xml.StartElement{ - Name: d.Name, - Attr: xattrs, - }) return nil } -func (d *UnknownTokensDecoder) End() { - d.AppendToken(xml.EndElement{Name: d.Name}) +func NewAttr3MF(namespace, parent string) Attr3MF { + if ext, ok := LoadExtension(namespace); ok { + return ext.NewAttr3MF(parent) + } + return &UnknownAttrs{ + Space: namespace, + } } -func (d *UnknownTokensDecoder) AppendToken(t xml.Token) { - d.tokens = append(d.tokens, t) +func LoadExtension(space string) (Spec, bool) { + specMu.RLock() + ext, ok := specs[space] + specMu.RUnlock() + return ext, ok } -func (d UnknownTokensDecoder) Tokens() UnknownTokens { - return d.tokens +func LoadValidator(ns string) (ValidateSpec, bool) { + specMu.RLock() + ext, ok := specs[ns] + specMu.RUnlock() + if ok { + ext, ok := ext.(ValidateSpec) + return ext, ok + } + return nil, false } diff --git a/spec/unknown.go b/spec/unknown.go new file mode 100644 index 0000000..1bfbeda --- /dev/null +++ b/spec/unknown.go @@ -0,0 +1,73 @@ +// © Copyright 2021 HP Development Company, L.P. +// SPDX-License Identifier: BSD-2-Clause + +package spec + +import "encoding/xml" + +// An UnknownAttrs represents a list of attributes +// that are not supported by any loaded Spec. +type UnknownAttrs struct { + Space string + Attr []xml.Attr +} + +func (u UnknownAttrs) Namespace() string { + return u.Space +} + +func (u UnknownAttrs) Marshal3MFAttr(enc Encoder, start *xml.StartElement) error { + start.Attr = append(start.Attr, u.Attr...) + return nil +} + +func (u *UnknownAttrs) Unmarshal3MFAttr(a XMLAttr) error { + u.Attr = append(u.Attr, xml.Attr{Name: a.Name, Value: string(a.Value)}) + return nil +} + +// UnknownTokens represents a section of an xml +// that cannot be decoded by any loaded Spec. +type UnknownTokens []xml.Token + +func (u UnknownTokens) Marshal3MF(enc Encoder) error { + for _, t := range u { + enc.EncodeToken(t) + } + return nil +} + +// UnknownTokensDecoder can be used by spec decoders to maintain the +// xml tree elements of unknown extensions. +type UnknownTokensDecoder struct { + Name xml.Name + + tokens UnknownTokens +} + +func (d *UnknownTokensDecoder) Start(attrs []XMLAttr) error { + var xattrs []xml.Attr + if len(attrs) > 0 { + xattrs = make([]xml.Attr, len(attrs)) + for i, att := range attrs { + xattrs[i] = xml.Attr{Name: att.Name, Value: string(att.Value)} + } + } + d.AppendToken(xml.StartElement{ + Name: d.Name, + Attr: xattrs, + }) + return nil +} + +func (d *UnknownTokensDecoder) End() { + d.AppendToken(xml.EndElement{Name: d.Name}) +} + +func (d *UnknownTokensDecoder) AppendToken(t xml.Token) { + d.tokens = append(d.tokens, t) +} + +func (d UnknownTokensDecoder) Tokens() UnknownTokens { + return d.tokens +} diff --git a/validate.go b/validate.go index c49a4b9..cdc0705 100644 --- a/validate.go +++ b/validate.go @@ -46,7 +46,7 @@ func (m *Model) Validate() error { errs = errors.Append(errs, checkMetadadata(m, m.Metadata)) for _, ext := range m.Extensions { - if ext, ok := loadValidator(ext.Namespace); ok { + if ext, ok := spec.LoadValidator(ext.Namespace); ok { errs = errors.Append(errs, ext.Validate(m, m.Path, m)) } } @@ -178,7 +178,7 @@ func (res *Resources) validate(m *Model, path string) error { } for _, ext := range m.Extensions { - if ext, ok := loadValidator(ext.Namespace); ok { + if ext, ok := spec.LoadValidator(ext.Namespace); ok { aErrs = errors.Append(aErrs, ext.Validate(m, path, r)) } } @@ -236,7 +236,7 @@ func (r *Object) Validate(m *Model, path string) error { } for _, ext := range m.Extensions { - if ext, ok := loadValidator(ext.Namespace); ok { + if ext, ok := spec.LoadValidator(ext.Namespace); ok { errs = errors.Append(errs, ext.Validate(m, path, r)) } } @@ -306,7 +306,7 @@ func (r *Object) validateComponents(m *Model, path string) error { func (m *Model) validateNamespaces() error { for _, ext := range m.Extensions { if ext.IsRequired { - if _, ok := loadExtension(ext.Namespace); !ok { + if _, ok := spec.LoadExtension(ext.Namespace); !ok { return errors.ErrRequiredExt } } diff --git a/validate_test.go b/validate_test.go index bdcb2f0..4572a49 100644 --- a/validate_test.go +++ b/validate_test.go @@ -12,10 +12,11 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf/errors" + "github.com/hpinc/go3mf/spec" ) func TestValidate(t *testing.T) { - Register(fakeSpec.Namespace, new(qmExtension)) + spec.Register(fakeSpec.Namespace, new(qmExtension)) tests := []struct { name string model *Model @@ -50,7 +51,7 @@ func TestValidate(t *testing.T) { {"build", &Model{Resources: Resources{Assets: []Asset{&BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}}, Objects: []*Object{ {ID: 2, Type: ObjectTypeOther, Mesh: &Mesh{Vertices: []Point3D{{}, {}, {}, {}}, Triangles: []Triangle{ {V1: 0, V2: 1, V3: 2}, {V1: 0, V2: 3, V3: 1}, {V1: 0, V2: 2, V3: 3}, {V1: 1, V2: 3, V3: 2}, - }}}}}, Build: Build{AnyAttr: AnyAttr{&fakeAttr{}}, Items: []*Item{ + }}}}}, Build: Build{AnyAttr: spec.AnyAttr{&fakeAttr{}}, Items: []*Item{ {}, {ObjectID: 2}, {ObjectID: 100}, From 0e0e049fb7179bca51be2f85ce898361afecfa77 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 1 Oct 2021 13:54:11 +0200 Subject: [PATCH 06/23] move Any to spec --- beamlattice/decoder.go | 2 +- beamlattice/decoder_test.go | 3 ++- beamlattice/encoder.go | 2 +- beamlattice/encoder_test.go | 3 ++- beamlattice/validate_test.go | 19 ++++++++-------- core.go | 9 +++----- decoder.go | 20 +++++------------ encoder.go | 41 ++++++++++++++--------------------- encoder_test.go | 6 ++--- materials/decoder.go | 2 +- materials/encoder.go | 10 ++++----- production/decoder.go | 2 +- production/encoder.go | 16 +++++++------- production/production_test.go | 8 +++---- read_test.go | 8 +++---- slices/decoder.go | 2 +- slices/encoder.go | 6 ++--- slices/slices_test.go | 2 +- spec/spec.go | 28 ++++++++++++++---------- spec/unknown.go | 14 ++++++++++-- 20 files changed, 100 insertions(+), 103 deletions(-) diff --git a/beamlattice/decoder.go b/beamlattice/decoder.go index 7efff71..6ead89b 100644 --- a/beamlattice/decoder.go +++ b/beamlattice/decoder.go @@ -16,7 +16,7 @@ func (Spec) NewAttr3MF(string) spec.Attr3MF { return nil } -func (Spec) CreateElementDecoder(parent interface{}, name string) spec.ElementDecoder { +func (Spec) NewElementDecoder(parent interface{}, name string) spec.ElementDecoder { if name == attrBeamLattice { return &beamLatticeDecoder{mesh: parent.(*go3mf.Mesh)} } diff --git a/beamlattice/decoder_test.go b/beamlattice/decoder_test.go index 46b80f9..ce337c0 100644 --- a/beamlattice/decoder_test.go +++ b/beamlattice/decoder_test.go @@ -10,6 +10,7 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf" "github.com/hpinc/go3mf/errors" + "github.com/hpinc/go3mf/spec" ) func TestDecode(t *testing.T) { @@ -17,7 +18,7 @@ func TestDecode(t *testing.T) { meshLattice := &go3mf.Object{ ID: 15, Name: "Box", Mesh: &go3mf.Mesh{ - Any: go3mf.Any{beamLattice}, + Any: spec.Any{beamLattice}, }, } beamLattice.MinLength = 0.0001 diff --git a/beamlattice/encoder.go b/beamlattice/encoder.go index 0038385..e4399e1 100644 --- a/beamlattice/encoder.go +++ b/beamlattice/encoder.go @@ -11,7 +11,7 @@ import ( ) // Marshal3MF encodes the resource. -func (m *BeamLattice) Marshal3MF(x spec.Encoder) error { +func (m *BeamLattice) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { xs := xml.StartElement{Name: xml.Name{Space: Namespace, Local: attrBeamLattice}, Attr: []xml.Attr{ {Name: xml.Name{Local: attrMinLength}, Value: strconv.FormatFloat(float64(m.MinLength), 'f', x.FloatPresicion(), 32)}, {Name: xml.Name{Local: attrRadius}, Value: strconv.FormatFloat(float64(m.Radius), 'f', x.FloatPresicion(), 32)}, diff --git a/beamlattice/encoder_test.go b/beamlattice/encoder_test.go index af8d969..9ba8f60 100644 --- a/beamlattice/encoder_test.go +++ b/beamlattice/encoder_test.go @@ -8,6 +8,7 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf" + "github.com/hpinc/go3mf/spec" ) func TestMarshalModel(t *testing.T) { @@ -16,7 +17,7 @@ func TestMarshalModel(t *testing.T) { ID: 15, Name: "Box", Mesh: &go3mf.Mesh{ Triangles: []go3mf.Triangle{}, - Any: go3mf.Any{beamLattice}}, + Any: spec.Any{beamLattice}}, } beamLattice.MinLength = 0.0001 beamLattice.CapMode = CapModeHemisphere diff --git a/beamlattice/validate_test.go b/beamlattice/validate_test.go index b3b7304..225b8c1 100644 --- a/beamlattice/validate_test.go +++ b/beamlattice/validate_test.go @@ -10,6 +10,7 @@ import ( "github.com/go-test/deep" "github.com/hpinc/go3mf" "github.com/hpinc/go3mf/errors" + "github.com/hpinc/go3mf/spec" ) func TestValidate(t *testing.T) { @@ -20,7 +21,7 @@ func TestValidate(t *testing.T) { }{ {"error in child", &go3mf.Model{Childs: map[string]*go3mf.ChildModel{ "/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{ - {ID: 1, Mesh: &go3mf.Mesh{Any: go3mf.Any{&BeamLattice{}}}}, + {ID: 1, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{}}}}, }}}, }}, []string{ fmt.Sprintf("/other.model@Resources@Object#0@Mesh: %v", errors.ErrInsufficientVertices), @@ -40,13 +41,13 @@ func TestValidate(t *testing.T) { fmt.Sprintf("Resources@Object#0@Components@Component#0: %v", errors.ErrMissingResource), }}, {"object incorret type", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ - {ID: 1, Type: go3mf.ObjectTypeOther, Mesh: &go3mf.Mesh{Any: go3mf.Any{&BeamLattice{ + {ID: 1, Type: go3mf.ObjectTypeOther, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClipMode: ClipInside, }}}}, - {ID: 2, Type: go3mf.ObjectTypeSurface, Mesh: &go3mf.Mesh{Any: go3mf.Any{&BeamLattice{ + {ID: 2, Type: go3mf.ObjectTypeSurface, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClipMode: ClipInside, }}}}, - {ID: 3, Type: go3mf.ObjectTypeSupport, Mesh: &go3mf.Mesh{Any: go3mf.Any{&BeamLattice{ + {ID: 3, Type: go3mf.ObjectTypeSupport, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClipMode: ClipInside, }}}}, }}}, []string{ @@ -55,11 +56,11 @@ func TestValidate(t *testing.T) { fmt.Sprintf("Resources@Object#2@Mesh@BeamLattice: %v", ErrLatticeObjType), }}, {"incorrect mesh references", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ - {ID: 1, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: go3mf.Any{nil}}}, - {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: go3mf.Any{&BeamLattice{ + {ID: 1, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{nil}}}, + {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClippingMeshID: 100, RepresentationMeshID: 2, }}}}, - {ID: 3, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: go3mf.Any{&BeamLattice{ + {ID: 3, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClippingMeshID: 1, RepresentationMeshID: 2, }}}}, }}}, []string{ @@ -68,7 +69,7 @@ func TestValidate(t *testing.T) { fmt.Sprintf("Resources@Object#2@Mesh@BeamLattice: %v", ErrLatticeInvalidMesh), }}, {"incorrect beams", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ - {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: go3mf.Any{&BeamLattice{ + {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClipMode: ClipInside, Beams: []Beam{ {}, {Indices: [2]uint32{1, 1}, Radius: [2]float32{0.5, 0}}, {Indices: [2]uint32{1, 3}}, }, @@ -80,7 +81,7 @@ func TestValidate(t *testing.T) { fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#2: %v", errors.ErrIndexOutOfBounds), }}, {"incorrect beamseat", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ - {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: go3mf.Any{&BeamLattice{ + {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClipMode: ClipInside, Beams: []Beam{ {Indices: [2]uint32{1, 2}}, }, BeamSets: []BeamSet{{Refs: []uint32{0, 2, 3}}}, diff --git a/core.go b/core.go index c46d3e7..728eebc 100644 --- a/core.go +++ b/core.go @@ -197,7 +197,7 @@ type Extension struct { type ChildModel struct { Resources Resources Relationships []Relationship - Any Any + Any spec.Any } // A Model is an in memory representation of the 3MF file. @@ -220,7 +220,7 @@ type Model struct { Childs map[string]*ChildModel // path -> child RootRelationships []Relationship Relationships []Relationship - Any Any + Any spec.Any AnyAttr spec.AnyAttr } @@ -472,7 +472,7 @@ type Mesh struct { Vertices []Point3D Triangles []Triangle AnyAttr spec.AnyAttr - Any Any + Any spec.Any } // BoundingBox returns the bounding box of the mesh. @@ -546,9 +546,6 @@ func newUnits(s string) (u Units, ok bool) { return } -// Any is an extension point containing information. -type Any []spec.Marshaler - const ( nsXML = "http://www.w3.org/XML/1998/namespace" nsXMLNs = "http://www.w3.org/2000/xmlns/" diff --git a/decoder.go b/decoder.go index 96f66cd..5f14041 100644 --- a/decoder.go +++ b/decoder.go @@ -37,9 +37,9 @@ func (d *modelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { } } } else if ext, ok := spec.LoadExtension(name.Space); ok { - child = ext.CreateElementDecoder(d.model, name.Local) + child = ext.NewElementDecoder(d.model, name.Local) } else { - child = &anyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.model.Any} + child = &spec.AnyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.model.Any} } return } @@ -295,7 +295,7 @@ func (d *resourceDecoder) Child(name xml.Name) (child spec.ElementDecoder) { child = &baseMaterialsDecoder{resources: d.resources} } } else if ext, ok := spec.LoadExtension(name.Space); ok { - child = ext.CreateElementDecoder(d.resources, name.Local) + child = ext.NewElementDecoder(d.resources, name.Local) } else { child = &unknownAssetDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, resources: d.resources} } @@ -423,9 +423,9 @@ func (d *meshDecoder) Child(name xml.Name) (child spec.ElementDecoder) { child = &trianglesDecoder{resource: d.resource} } } else if ext, ok := spec.LoadExtension(name.Space); ok { - child = ext.CreateElementDecoder(d.resource.Mesh, name.Local) + child = ext.NewElementDecoder(d.resource.Mesh, name.Local) } else { - child = &anyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.resource.Mesh.Any} + child = &spec.AnyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.resource.Mesh.Any} } return } @@ -793,13 +793,3 @@ func (d *unknownAssetDecoder) End() { d.resource.UnknownTokens = d.UnknownTokensDecoder.Tokens() d.resources.Assets = append(d.resources.Assets, &d.resource) } - -type anyUnknownDecoder struct { - spec.UnknownTokensDecoder - Any *Any -} - -func (d *anyUnknownDecoder) End() { - d.UnknownTokensDecoder.End() - *d.Any = append(*d.Any, d.Tokens()) -} diff --git a/encoder.go b/encoder.go index f584680..f37640f 100644 --- a/encoder.go +++ b/encoder.go @@ -234,7 +234,7 @@ func (e *Encoder) modelToken(x spec.Encoder, m *Model, isRoot bool) (xml.StartEl attrs = append(attrs, xml.Attr{Name: xml.Name{Local: attrReqExt}, Value: strings.Join(exts, " ")}) } tm := xml.StartElement{Name: xml.Name{Local: attrModel}, Attr: attrs} - m.AnyAttr.Marshal3MFAttr(x, &tm) + m.AnyAttr.Marshal3MF(x, &tm) return tm, nil } @@ -249,7 +249,7 @@ func (e *Encoder) writeChildModel(x spec.Encoder, m *Model, child *ChildModel) e xb := xml.StartElement{Name: xml.Name{Local: attrBuild}} x.EncodeToken(xb) x.EncodeToken(xb.End()) - child.Any.encode(x) + child.Any.Marshal3MF(x, &tm) x.EncodeToken(tm.End()) return x.Flush() } @@ -266,7 +266,7 @@ func (e *Encoder) writeModel(x spec.Encoder, m *Model) error { return err } e.writeBuild(x, m) - m.Any.encode(x) + m.Any.Marshal3MF(x, &tm) x.EncodeToken(tm.End()) return x.Flush() } @@ -280,7 +280,7 @@ func (e *Encoder) writeMetadataGroup(x spec.Encoder, m []Metadata) { func (e *Encoder) writeBuild(x spec.Encoder, m *Model) { xb := xml.StartElement{Name: xml.Name{Local: attrBuild}} - m.Build.AnyAttr.Marshal3MFAttr(x, &xb) + m.Build.AnyAttr.Marshal3MF(x, &xb) x.EncodeToken(xb) x.SetAutoClose(true) for _, item := range m.Build.Items { @@ -297,7 +297,7 @@ func (e *Encoder) writeBuild(x spec.Encoder, m *Model) { Name: xml.Name{Local: attrPartNumber}, Value: item.PartNumber, }) } - item.AnyAttr.Marshal3MFAttr(x, &xi) + item.AnyAttr.Marshal3MF(x, &xi) if len(item.Metadata) != 0 { x.SetAutoClose(false) x.EncodeToken(xi) @@ -314,11 +314,11 @@ func (e *Encoder) writeBuild(x spec.Encoder, m *Model) { func (e *Encoder) writeResources(x spec.Encoder, rs *Resources) error { xt := xml.StartElement{Name: xml.Name{Local: attrResources}} - rs.AnyAttr.Marshal3MFAttr(x, &xt) + rs.AnyAttr.Marshal3MF(x, &xt) x.EncodeToken(xt) for _, r := range rs.Assets { if r, ok := r.(spec.Marshaler); ok { - if err := r.Marshal3MF(x); err != nil { + if err := r.Marshal3MF(x, &xt); err != nil { return err } } @@ -391,7 +391,7 @@ func (e *Encoder) writeObject(x spec.Encoder, r *Object) { }) } } - r.AnyAttr.Marshal3MFAttr(x, &xo) + r.AnyAttr.Marshal3MF(x, &xo) x.EncodeToken(xo) if len(r.Metadata) != 0 { @@ -408,7 +408,7 @@ func (e *Encoder) writeObject(x spec.Encoder, r *Object) { func (e *Encoder) writeComponents(x spec.Encoder, comps *Components) { xcs := xml.StartElement{Name: xml.Name{Local: attrComponents}} - comps.AnyAttr.Marshal3MFAttr(x, &xcs) + comps.AnyAttr.Marshal3MF(x, &xcs) x.EncodeToken(xcs) x.SetAutoClose(true) for _, c := range comps.Component { @@ -420,7 +420,7 @@ func (e *Encoder) writeComponents(x spec.Encoder, comps *Components) { if c.HasTransform() { xt.Attr = append(xt.Attr, xml.Attr{Name: xml.Name{Local: attrTransform}, Value: c.Transform.String()}) } - c.AnyAttr.Marshal3MFAttr(x, &xt) + c.AnyAttr.Marshal3MF(x, &xt) x.EncodeToken(xt) } x.SetAutoClose(false) @@ -487,7 +487,7 @@ func (e *Encoder) writeTriangles(x spec.Encoder, r *Object, m *Mesh) { start.Attr = attrs[:5] } } - t.AnyAttr.Marshal3MFAttr(x, &start) + t.AnyAttr.Marshal3MF(x, &start) x.EncodeToken(start) } x.SetSkipAttrEscape(false) @@ -497,21 +497,21 @@ func (e *Encoder) writeTriangles(x spec.Encoder, r *Object, m *Mesh) { func (e *Encoder) writeMesh(x spec.Encoder, r *Object, m *Mesh) { xm := xml.StartElement{Name: xml.Name{Local: attrMesh}} - m.AnyAttr.Marshal3MFAttr(x, &xm) + m.AnyAttr.Marshal3MF(x, &xm) x.EncodeToken(xm) e.writeVertices(x, m) e.writeTriangles(x, r, m) - m.Any.encode(x) + m.Any.Marshal3MF(x, &xm) x.EncodeToken(xm.End()) } -func (r *BaseMaterials) Marshal3MF(x spec.Encoder) error { +func (r *BaseMaterials) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { xt := xml.StartElement{Name: xml.Name{Local: attrBaseMaterials}, Attr: []xml.Attr{ {Name: xml.Name{Local: attrID}, Value: strconv.FormatUint(uint64(r.ID), 10)}, }} - r.AnyAttr.Marshal3MFAttr(x, &xt) + r.AnyAttr.Marshal3MF(x, &xt) x.EncodeToken(xt) x.SetAutoClose(true) for _, ma := range r.Materials { @@ -522,19 +522,10 @@ func (r *BaseMaterials) Marshal3MF(x spec.Encoder) error { {Name: xml.Name{Local: attrDisplayColor}, Value: spec.FormatRGBA(ma.Color)}, }, } - ma.AnyAttr.Marshal3MFAttr(x, &start) + ma.AnyAttr.Marshal3MF(x, &start) x.EncodeToken(start) } x.SetAutoClose(false) x.EncodeToken(xt.End()) return nil } - -func (e Any) encode(x spec.Encoder) error { - for _, ext := range e { - if err := ext.Marshal3MF(x); err == nil { - return err - } - } - return nil -} diff --git a/encoder_test.go b/encoder_test.go index 23f1822..89f3d5e 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -18,7 +18,7 @@ import ( ) // Marshal3MF encodes the resource. -func (f *fakeAsset) Marshal3MF(x spec.Encoder) error { +func (f *fakeAsset) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { xs := xml.StartElement{Name: xml.Name{Space: fakeExtension, Local: "fakeasset"}, Attr: []xml.Attr{ {Name: xml.Name{Local: attrID}, Value: strconv.FormatUint(uint64(f.ID), 10)}, }} @@ -46,7 +46,7 @@ func TestMarshalModel(t *testing.T) { Units: UnitMillimeter, Language: "en-US", Path: "/3D/3dmodel.model", Thumbnail: "/thumbnail.png", Extensions: []Extension{fakeSpec, fooSpec}, AnyAttr: spec.AnyAttr{&fakeAttr{Value: "model_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo1"}}}}, - Any: Any{spec.UnknownTokens{ + Any: spec.Any{spec.UnknownTokens{ xml.StartElement{Name: fooName}, xml.EndElement{Name: fooName}, }}, @@ -68,7 +68,7 @@ func TestMarshalModel(t *testing.T) { ID: 8, Name: "Box 1", PartNumber: "11111111-1111-1111-1111-111111111111", Thumbnail: "/a.png", AnyAttr: spec.AnyAttr{&fakeAttr{Value: "object_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo3"}}}}, PID: 1, PIndex: 1, Type: ObjectTypeModel, Mesh: &Mesh{ - Any: Any{spec.UnknownTokens{ + Any: spec.Any{spec.UnknownTokens{ xml.StartElement{Name: fooName}, xml.EndElement{Name: fooName}, }}, diff --git a/materials/decoder.go b/materials/decoder.go index 6b78f38..ccc9afb 100644 --- a/materials/decoder.go +++ b/materials/decoder.go @@ -17,7 +17,7 @@ func (Spec) NewAttr3MF(string) spec.Attr3MF { return nil } -func (Spec) CreateElementDecoder(parent interface{}, name string) (child spec.ElementDecoder) { +func (Spec) NewElementDecoder(parent interface{}, name string) (child spec.ElementDecoder) { switch name { case attrColorGroup: child = &colorGroupDecoder{resources: parent.(*go3mf.Resources)} diff --git a/materials/encoder.go b/materials/encoder.go index 9e5d7fb..53f361d 100644 --- a/materials/encoder.go +++ b/materials/encoder.go @@ -12,7 +12,7 @@ import ( ) // Marshal3MF encodes the resource. -func (r *ColorGroup) Marshal3MF(x spec.Encoder) error { +func (r *ColorGroup) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { xs := xml.StartElement{Name: xml.Name{Space: Namespace, Local: attrColorGroup}, Attr: []xml.Attr{ {Name: xml.Name{Local: attrID}, Value: strconv.FormatUint(uint64(r.ID), 10)}, }} @@ -33,7 +33,7 @@ func (r *ColorGroup) Marshal3MF(x spec.Encoder) error { } // Marshal3MF encodes the resource. -func (r *Texture2DGroup) Marshal3MF(x spec.Encoder) error { +func (r *Texture2DGroup) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { xs := xml.StartElement{Name: xml.Name{Space: Namespace, Local: attrTexture2DGroup}, Attr: []xml.Attr{ {Name: xml.Name{Local: attrID}, Value: strconv.FormatUint(uint64(r.ID), 10)}, {Name: xml.Name{Local: attrTexID}, Value: strconv.FormatUint(uint64(r.TextureID), 10)}, @@ -58,7 +58,7 @@ func (r *Texture2DGroup) Marshal3MF(x spec.Encoder) error { } // Marshal3MF encodes the resource. -func (r *CompositeMaterials) Marshal3MF(x spec.Encoder) error { +func (r *CompositeMaterials) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { indices := make([]string, len(r.Indices)) for i, idx := range r.Indices { indices[i] = strconv.FormatUint(uint64(idx), 10) @@ -87,7 +87,7 @@ func (r *CompositeMaterials) Marshal3MF(x spec.Encoder) error { } // Marshal3MF encodes the resource. -func (r *MultiProperties) Marshal3MF(x spec.Encoder) error { +func (r *MultiProperties) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { pids := make([]string, len(r.PIDs)) for i, idx := range r.PIDs { pids[i] = strconv.FormatUint(uint64(idx), 10) @@ -120,7 +120,7 @@ func (r *MultiProperties) Marshal3MF(x spec.Encoder) error { } // Marshal3MF encodes the resource. -func (r *Texture2D) Marshal3MF(x spec.Encoder) error { +func (r *Texture2D) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { x.AddRelationship(spec.Relationship{Path: r.Path, Type: RelTypeTexture3D}) xs := xml.StartElement{Name: xml.Name{Space: Namespace, Local: attrTexture2D}, Attr: []xml.Attr{ {Name: xml.Name{Local: attrID}, Value: strconv.FormatUint(uint64(r.ID), 10)}, diff --git a/production/decoder.go b/production/decoder.go index 61c7c25..75052a4 100644 --- a/production/decoder.go +++ b/production/decoder.go @@ -9,7 +9,7 @@ import ( "github.com/hpinc/go3mf/uuid" ) -func (Spec) CreateElementDecoder(_ interface{}, _ string) spec.ElementDecoder { +func (Spec) NewElementDecoder(_ interface{}, _ string) spec.ElementDecoder { return nil } diff --git a/production/encoder.go b/production/encoder.go index 71b0c9e..7d41027 100644 --- a/production/encoder.go +++ b/production/encoder.go @@ -9,20 +9,20 @@ import ( "github.com/hpinc/go3mf/spec" ) -// Marshal3MFAttr encodes the resource attributes. -func (u *BuildAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { +// Marshal3MF encodes the resource attributes. +func (u *BuildAttr) Marshal3MF(_ spec.Encoder, start *xml.StartElement) error { start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: u.UUID}) return nil } -// Marshal3MFAttr encodes the resource attributes. -func (u *ObjectAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { +// Marshal3MF encodes the resource attributes. +func (u *ObjectAttr) Marshal3MF(_ spec.Encoder, start *xml.StartElement) error { start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrProdUUID}, Value: u.UUID}) return nil } -// Marshal3MFAttr encodes the resource attributes. -func (u *ItemAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { +// Marshal3MF encodes the resource attributes. +func (u *ItemAttr) Marshal3MF(_ spec.Encoder, start *xml.StartElement) error { if u.Path != "" { start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrPath}, Value: u.Path}) } @@ -30,8 +30,8 @@ func (u *ItemAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error return nil } -// Marshal3MFAttr encodes the resource attributes. -func (u *ComponentAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { +// Marshal3MF encodes the resource attributes. +func (u *ComponentAttr) Marshal3MF(_ spec.Encoder, start *xml.StartElement) error { if u.Path != "" { start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrPath}, Value: u.Path}) } diff --git a/production/production_test.go b/production/production_test.go index 69dd69d..c4c89f0 100644 --- a/production/production_test.go +++ b/production/production_test.go @@ -10,10 +10,10 @@ import ( "github.com/hpinc/go3mf/spec" ) -var _ spec.MarshalerAttr = new(BuildAttr) -var _ spec.MarshalerAttr = new(ItemAttr) -var _ spec.MarshalerAttr = new(ComponentAttr) -var _ spec.MarshalerAttr = new(ObjectAttr) +var _ spec.Marshaler = new(BuildAttr) +var _ spec.Marshaler = new(ItemAttr) +var _ spec.Marshaler = new(ComponentAttr) +var _ spec.Marshaler = new(ObjectAttr) func TestComponentAttr_ObjectPath(t *testing.T) { tests := []struct { diff --git a/read_test.go b/read_test.go index 48ff86d..9387576 100644 --- a/read_test.go +++ b/read_test.go @@ -46,7 +46,7 @@ func (qmExtension) NewAttr3MF(parent string) spec.Attr3MF { return &fakeAttr{} } -func (qmExtension) CreateElementDecoder(parent interface{}, name string) spec.ElementDecoder { +func (qmExtension) NewElementDecoder(parent interface{}, name string) spec.ElementDecoder { if e, ok := parent.(*Resources); ok { return &fakeAssetDecoder{resources: e} } @@ -83,7 +83,7 @@ func (f *fakeAttr) ObjectPath() string { return f.Value } func (f fakeAttr) Namespace() string { return fakeExtension } -func (f fakeAttr) Marshal3MFAttr(enc spec.Encoder, start *xml.StartElement) error { +func (f fakeAttr) Marshal3MF(enc spec.Encoder, start *xml.StartElement) error { start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: fakeExtension, Local: "value"}, Value: f.Value}) return nil } @@ -326,7 +326,7 @@ func TestDecoder_processRootModel(t *testing.T) { ID: 8, Name: "Box 1", Thumbnail: "/a.png", PID: 5, PartNumber: "11111111-1111-1111-1111-111111111111", Mesh: &Mesh{ AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval9"}}}}, - Any: Any{spec.UnknownTokens{ + Any: spec.Any{spec.UnknownTokens{ xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "fake"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "fake"}}, }}, @@ -400,7 +400,7 @@ func TestDecoder_processRootModel(t *testing.T) { AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval1"}}}}, }, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval"}}}}, - Any: Any{ + Any: spec.Any{ spec.UnknownTokens{ xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "other"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "other"}}, diff --git a/slices/decoder.go b/slices/decoder.go index 0e15b89..89ab56d 100644 --- a/slices/decoder.go +++ b/slices/decoder.go @@ -12,7 +12,7 @@ import ( "github.com/hpinc/go3mf/spec" ) -func (Spec) CreateElementDecoder(parent interface{}, name string) spec.ElementDecoder { +func (Spec) NewElementDecoder(parent interface{}, name string) spec.ElementDecoder { if name == attrSliceStack { return &sliceStackDecoder{resources: parent.(*go3mf.Resources)} } diff --git a/slices/encoder.go b/slices/encoder.go index d186ebc..9093aad 100644 --- a/slices/encoder.go +++ b/slices/encoder.go @@ -11,8 +11,8 @@ import ( "github.com/hpinc/go3mf/spec" ) -// Marshal3MFAttr encodes the resource attributes. -func (s *ObjectAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) error { +// Marshal3MF encodes the resource attributes. +func (s *ObjectAttr) Marshal3MF(_ spec.Encoder, start *xml.StartElement) error { start.Attr = append(start.Attr, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrSliceRefID}, Value: strconv.FormatUint(uint64(s.SliceStackID), 10)}, xml.Attr{Name: xml.Name{Space: Namespace, Local: attrMeshRes}, Value: s.MeshResolution.String()}, @@ -21,7 +21,7 @@ func (s *ObjectAttr) Marshal3MFAttr(_ spec.Encoder, start *xml.StartElement) err } // Marshal3MF encodes the resource. -func (s *SliceStack) Marshal3MF(x spec.Encoder) error { +func (s *SliceStack) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { xs := xml.StartElement{Name: xml.Name{Space: Namespace, Local: attrSliceStack}, Attr: []xml.Attr{ {Name: xml.Name{Local: attrID}, Value: strconv.FormatUint(uint64(s.ID), 10)}, }} diff --git a/slices/slices_test.go b/slices/slices_test.go index 25aa892..9c757cd 100644 --- a/slices/slices_test.go +++ b/slices/slices_test.go @@ -13,7 +13,7 @@ import ( var _ go3mf.Asset = new(SliceStack) var _ spec.Marshaler = new(SliceStack) -var _ spec.MarshalerAttr = new(ObjectAttr) +var _ spec.Marshaler = new(ObjectAttr) var _ spec.Spec = new(Spec) func TestSliceStack_Identify(t *testing.T) { diff --git a/spec/spec.go b/spec/spec.go index 8be0c1a..a7610dc 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -13,7 +13,7 @@ import ( // Specs may implement ValidateSpec. type Spec interface { NewAttr3MF(parent string) Attr3MF - CreateElementDecoder(parent interface{}, name string) ElementDecoder + NewElementDecoder(parent interface{}, name string) ElementDecoder } type PropertyGroup interface { @@ -45,7 +45,7 @@ type Relationship struct { // Marshaler is the interface implemented by objects // that can marshal themselves into valid XML elements. type Marshaler interface { - Marshal3MF(Encoder) error + Marshal3MF(Encoder, *xml.StartElement) error } // UnmarshalerAttr is the interface implemented by objects that can unmarshal @@ -54,12 +54,6 @@ type UnmarshalerAttr interface { Unmarshal3MFAttr(XMLAttr) error } -// MarshalerAttr is the interface implemented by objects that can marshal -// themselves into valid XML attributes. -type MarshalerAttr interface { - Marshal3MFAttr(Encoder, *xml.StartElement) error -} - type ErrorWrapper interface { Wrap(error) error } @@ -122,7 +116,7 @@ func Register(namespace string, spec Spec) { type Attr3MF interface { UnmarshalerAttr - MarshalerAttr + Marshaler Namespace() string } @@ -137,9 +131,9 @@ func (a AnyAttr) Get(namespace string) Attr3MF { return nil } -func (a AnyAttr) Marshal3MFAttr(x Encoder, start *xml.StartElement) error { +func (a AnyAttr) Marshal3MF(x Encoder, start *xml.StartElement) error { for _, ext := range a { - err := ext.Marshal3MFAttr(x, start) + err := ext.Marshal3MF(x, start) if err != nil { return err } @@ -156,6 +150,18 @@ func NewAttr3MF(namespace, parent string) Attr3MF { } } +// Any is an extension point containing information. +type Any []Marshaler + +func (e Any) Marshal3MF(x Encoder, start *xml.StartElement) error { + for _, ext := range e { + if err := ext.Marshal3MF(x, start); err == nil { + return err + } + } + return nil +} + func LoadExtension(space string) (Spec, bool) { specMu.RLock() ext, ok := specs[space] diff --git a/spec/unknown.go b/spec/unknown.go index 1bfbeda..7db29df 100644 --- a/spec/unknown.go +++ b/spec/unknown.go @@ -16,7 +16,7 @@ func (u UnknownAttrs) Namespace() string { return u.Space } -func (u UnknownAttrs) Marshal3MFAttr(enc Encoder, start *xml.StartElement) error { +func (u UnknownAttrs) Marshal3MF(enc Encoder, start *xml.StartElement) error { start.Attr = append(start.Attr, u.Attr...) return nil } @@ -30,7 +30,7 @@ func (u *UnknownAttrs) Unmarshal3MFAttr(a XMLAttr) error { // that cannot be decoded by any loaded Spec. type UnknownTokens []xml.Token -func (u UnknownTokens) Marshal3MF(enc Encoder) error { +func (u UnknownTokens) Marshal3MF(enc Encoder, _ *xml.StartElement) error { for _, t := range u { enc.EncodeToken(t) } @@ -71,3 +71,13 @@ func (d *UnknownTokensDecoder) AppendToken(t xml.Token) { func (d UnknownTokensDecoder) Tokens() UnknownTokens { return d.tokens } + +type AnyUnknownDecoder struct { + UnknownTokensDecoder + Any *Any +} + +func (d *AnyUnknownDecoder) End() { + d.UnknownTokensDecoder.End() + *d.Any = append(*d.Any, d.Tokens()) +} From 6f61d1999aa4fd529234d247db8c85ef2138e7f5 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 1 Oct 2021 14:01:47 +0200 Subject: [PATCH 07/23] rename Attr3MF to AttrGroup --- beamlattice/decoder.go | 2 +- decoder.go | 22 +++++++++++----------- materials/decoder.go | 2 +- production/decoder.go | 2 +- read_test.go | 2 +- slices/decoder.go | 2 +- spec/spec.go | 12 +++++++----- 7 files changed, 23 insertions(+), 21 deletions(-) diff --git a/beamlattice/decoder.go b/beamlattice/decoder.go index 6ead89b..ea66163 100644 --- a/beamlattice/decoder.go +++ b/beamlattice/decoder.go @@ -12,7 +12,7 @@ import ( "github.com/hpinc/go3mf/spec" ) -func (Spec) NewAttr3MF(string) spec.Attr3MF { +func (Spec) NewAttr3MF(string) spec.AttrGroup { return nil } diff --git a/decoder.go b/decoder.go index 5f14041..0339f50 100644 --- a/decoder.go +++ b/decoder.go @@ -92,7 +92,7 @@ func (d *modelDecoder) noCoreAttribute(a spec.XMLAttr) (err error) { IsRequired: false, }) default: - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = d.model.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrModel) d.model.AnyAttr = append(d.model.AnyAttr, attr) @@ -185,7 +185,7 @@ func (d *buildDecoder) Wrap(err error) error { func (d *buildDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = d.build.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrBuild) d.build.AnyAttr = append(d.build.AnyAttr, attr) @@ -226,7 +226,7 @@ func (d *buildItemDecoder) Start(attrs []spec.XMLAttr) error { if a.Name.Space == "" { errs = specerr.Append(errs, d.parseCoreAttr(a)) } else { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = d.item.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrItem) d.item.AnyAttr = append(d.item.AnyAttr, attr) @@ -269,7 +269,7 @@ type resourceDecoder struct { func (d *resourceDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = d.resources.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrResources) d.resources.AnyAttr = append(d.resources.AnyAttr, attr) @@ -337,7 +337,7 @@ func (d *baseMaterialsDecoder) Start(attrs []spec.XMLAttr) error { d.resource.ID = uint32(id) } } else { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = d.resource.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrBaseMaterials) d.resource.AnyAttr = append(d.resource.AnyAttr, attr) @@ -374,7 +374,7 @@ func (d *baseMaterialDecoder) Start(attrs []spec.XMLAttr) error { } } } else { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = base.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrBase) base.AnyAttr = append(base.AnyAttr, attr) @@ -398,7 +398,7 @@ func (d *meshDecoder) Start(attrs []spec.XMLAttr) error { d.resource.Mesh = new(Mesh) var errs error for _, a := range attrs { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = d.resource.Mesh.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrMesh) d.resource.Mesh.AnyAttr = append(d.resource.Mesh.AnyAttr, attr) @@ -552,7 +552,7 @@ func (d *triangleDecoder) Start(attrs []spec.XMLAttr) error { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, required)) } } else { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = t.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrTriangle) t.AnyAttr = append(t.AnyAttr, attr) @@ -598,7 +598,7 @@ func (d *objectDecoder) Start(attrs []spec.XMLAttr) error { if a.Name.Space == "" { errs = specerr.Append(errs, d.parseCoreAttr(a)) } else { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = d.resource.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrObject) d.resource.AnyAttr = append(d.resource.AnyAttr, attr) @@ -677,7 +677,7 @@ func (d *componentsDecoder) Start(attrs []spec.XMLAttr) error { d.componentDecoder.resource = d.resource for _, a := range attrs { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = components.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrComponents) components.AnyAttr = append(components.AnyAttr, attr) @@ -728,7 +728,7 @@ func (d *componentDecoder) Start(attrs []spec.XMLAttr) error { } } } else { - var attr spec.Attr3MF + var attr spec.AttrGroup if attr = component.AnyAttr.Get(a.Name.Space); attr == nil { attr = spec.NewAttr3MF(a.Name.Space, attrComponent) component.AnyAttr = append(component.AnyAttr, attr) diff --git a/materials/decoder.go b/materials/decoder.go index ccc9afb..566ea0c 100644 --- a/materials/decoder.go +++ b/materials/decoder.go @@ -13,7 +13,7 @@ import ( "github.com/hpinc/go3mf/spec" ) -func (Spec) NewAttr3MF(string) spec.Attr3MF { +func (Spec) NewAttr3MF(string) spec.AttrGroup { return nil } diff --git a/production/decoder.go b/production/decoder.go index 75052a4..b7fd14d 100644 --- a/production/decoder.go +++ b/production/decoder.go @@ -13,7 +13,7 @@ func (Spec) NewElementDecoder(_ interface{}, _ string) spec.ElementDecoder { return nil } -func (Spec) NewAttr3MF(parent string) spec.Attr3MF { +func (Spec) NewAttr3MF(parent string) spec.AttrGroup { switch parent { case "build": return new(BuildAttr) diff --git a/read_test.go b/read_test.go index 9387576..bfb0ec0 100644 --- a/read_test.go +++ b/read_test.go @@ -42,7 +42,7 @@ var fooSpec = Extension{ type qmExtension struct{} -func (qmExtension) NewAttr3MF(parent string) spec.Attr3MF { +func (qmExtension) NewAttr3MF(parent string) spec.AttrGroup { return &fakeAttr{} } diff --git a/slices/decoder.go b/slices/decoder.go index 89ab56d..4a2f756 100644 --- a/slices/decoder.go +++ b/slices/decoder.go @@ -19,7 +19,7 @@ func (Spec) NewElementDecoder(parent interface{}, name string) spec.ElementDecod return nil } -func (Spec) NewAttr3MF(parentNode string) spec.Attr3MF { +func (Spec) NewAttr3MF(parentNode string) spec.AttrGroup { switch parentNode { case "object": return new(ObjectAttr) diff --git a/spec/spec.go b/spec/spec.go index a7610dc..c691137 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -12,7 +12,7 @@ import ( // // Specs may implement ValidateSpec. type Spec interface { - NewAttr3MF(parent string) Attr3MF + NewAttr3MF(parent string) AttrGroup NewElementDecoder(parent interface{}, name string) ElementDecoder } @@ -114,15 +114,17 @@ func Register(namespace string, spec Spec) { specs[namespace] = spec } -type Attr3MF interface { +// AttrGroup defines a container for different attributes of the same namespace. +// It supports encoding and decoding to XML. +type AttrGroup interface { UnmarshalerAttr Marshaler Namespace() string } -type AnyAttr []Attr3MF +type AnyAttr []AttrGroup -func (a AnyAttr) Get(namespace string) Attr3MF { +func (a AnyAttr) Get(namespace string) AttrGroup { for _, v := range a { if v.Namespace() == namespace { return v @@ -141,7 +143,7 @@ func (a AnyAttr) Marshal3MF(x Encoder, start *xml.StartElement) error { return nil } -func NewAttr3MF(namespace, parent string) Attr3MF { +func NewAttr3MF(namespace, parent string) AttrGroup { if ext, ok := LoadExtension(namespace); ok { return ext.NewAttr3MF(parent) } From bc6a4e8545b5e0cb2ddc8f855d01d2d6b5768521 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 1 Oct 2021 14:18:36 +0200 Subject: [PATCH 08/23] use xml.Name in NewAttrGroup --- beamlattice/decoder.go | 2 +- decoder.go | 28 ++++----- materials/decoder.go | 2 +- production/decoder.go | 25 +++++--- read_test.go | 6 +- slices/decoder.go | 10 +-- spec/encoding.go | 60 ++++++++++++++++++ spec/spec.go | 134 ++++++++++++----------------------------- validate.go | 2 +- 9 files changed, 141 insertions(+), 128 deletions(-) create mode 100644 spec/encoding.go diff --git a/beamlattice/decoder.go b/beamlattice/decoder.go index ea66163..385a165 100644 --- a/beamlattice/decoder.go +++ b/beamlattice/decoder.go @@ -12,7 +12,7 @@ import ( "github.com/hpinc/go3mf/spec" ) -func (Spec) NewAttr3MF(string) spec.AttrGroup { +func (Spec) NewAttrGroup(xml.Name) spec.AttrGroup { return nil } diff --git a/decoder.go b/decoder.go index 0339f50..93f57a6 100644 --- a/decoder.go +++ b/decoder.go @@ -36,7 +36,7 @@ func (d *modelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { child = &metadataDecoder{metadatas: &d.model.Metadata, model: d.model} } } - } else if ext, ok := spec.LoadExtension(name.Space); ok { + } else if ext, ok := spec.Load(name.Space); ok { child = ext.NewElementDecoder(d.model, name.Local) } else { child = &spec.AnyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.model.Any} @@ -94,7 +94,7 @@ func (d *modelDecoder) noCoreAttribute(a spec.XMLAttr) (err error) { default: var attr spec.AttrGroup if attr = d.model.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrModel) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrModel}) d.model.AnyAttr = append(d.model.AnyAttr, attr) } err = specerr.Append(err, attr.Unmarshal3MFAttr(a)) @@ -187,7 +187,7 @@ func (d *buildDecoder) Start(attrs []spec.XMLAttr) error { for _, a := range attrs { var attr spec.AttrGroup if attr = d.build.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrBuild) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrBuild}) d.build.AnyAttr = append(d.build.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) @@ -228,7 +228,7 @@ func (d *buildItemDecoder) Start(attrs []spec.XMLAttr) error { } else { var attr spec.AttrGroup if attr = d.item.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrItem) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrItem}) d.item.AnyAttr = append(d.item.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) @@ -271,7 +271,7 @@ func (d *resourceDecoder) Start(attrs []spec.XMLAttr) error { for _, a := range attrs { var attr spec.AttrGroup if attr = d.resources.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrResources) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrResources}) d.resources.AnyAttr = append(d.resources.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) @@ -294,7 +294,7 @@ func (d *resourceDecoder) Child(name xml.Name) (child spec.ElementDecoder) { case attrBaseMaterials: child = &baseMaterialsDecoder{resources: d.resources} } - } else if ext, ok := spec.LoadExtension(name.Space); ok { + } else if ext, ok := spec.Load(name.Space); ok { child = ext.NewElementDecoder(d.resources, name.Local) } else { child = &unknownAssetDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, resources: d.resources} @@ -339,7 +339,7 @@ func (d *baseMaterialsDecoder) Start(attrs []spec.XMLAttr) error { } else { var attr spec.AttrGroup if attr = d.resource.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrBaseMaterials) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrBaseMaterials}) d.resource.AnyAttr = append(d.resource.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) @@ -376,7 +376,7 @@ func (d *baseMaterialDecoder) Start(attrs []spec.XMLAttr) error { } else { var attr spec.AttrGroup if attr = base.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrBase) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrBase}) base.AnyAttr = append(base.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) @@ -400,7 +400,7 @@ func (d *meshDecoder) Start(attrs []spec.XMLAttr) error { for _, a := range attrs { var attr spec.AttrGroup if attr = d.resource.Mesh.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrMesh) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrMesh}) d.resource.Mesh.AnyAttr = append(d.resource.Mesh.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) @@ -422,7 +422,7 @@ func (d *meshDecoder) Child(name xml.Name) (child spec.ElementDecoder) { } else if name.Local == attrTriangles { child = &trianglesDecoder{resource: d.resource} } - } else if ext, ok := spec.LoadExtension(name.Space); ok { + } else if ext, ok := spec.Load(name.Space); ok { child = ext.NewElementDecoder(d.resource.Mesh, name.Local) } else { child = &spec.AnyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.resource.Mesh.Any} @@ -554,7 +554,7 @@ func (d *triangleDecoder) Start(attrs []spec.XMLAttr) error { } else { var attr spec.AttrGroup if attr = t.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrTriangle) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrTriangle}) t.AnyAttr = append(t.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) @@ -600,7 +600,7 @@ func (d *objectDecoder) Start(attrs []spec.XMLAttr) error { } else { var attr spec.AttrGroup if attr = d.resource.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrObject) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrObject}) d.resource.AnyAttr = append(d.resource.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) @@ -679,7 +679,7 @@ func (d *componentsDecoder) Start(attrs []spec.XMLAttr) error { for _, a := range attrs { var attr spec.AttrGroup if attr = components.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrComponents) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrComponents}) components.AnyAttr = append(components.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) @@ -730,7 +730,7 @@ func (d *componentDecoder) Start(attrs []spec.XMLAttr) error { } else { var attr spec.AttrGroup if attr = component.AnyAttr.Get(a.Name.Space); attr == nil { - attr = spec.NewAttr3MF(a.Name.Space, attrComponent) + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrComponent}) component.AnyAttr = append(component.AnyAttr, attr) } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) diff --git a/materials/decoder.go b/materials/decoder.go index 566ea0c..59aa78a 100644 --- a/materials/decoder.go +++ b/materials/decoder.go @@ -13,7 +13,7 @@ import ( "github.com/hpinc/go3mf/spec" ) -func (Spec) NewAttr3MF(string) spec.AttrGroup { +func (Spec) NewAttrGroup(xml.Name) spec.AttrGroup { return nil } diff --git a/production/decoder.go b/production/decoder.go index b7fd14d..7fa2865 100644 --- a/production/decoder.go +++ b/production/decoder.go @@ -4,6 +4,9 @@ package production import ( + "encoding/xml" + + "github.com/hpinc/go3mf" specerr "github.com/hpinc/go3mf/errors" "github.com/hpinc/go3mf/spec" "github.com/hpinc/go3mf/uuid" @@ -13,16 +16,18 @@ func (Spec) NewElementDecoder(_ interface{}, _ string) spec.ElementDecoder { return nil } -func (Spec) NewAttr3MF(parent string) spec.AttrGroup { - switch parent { - case "build": - return new(BuildAttr) - case "item": - return new(ItemAttr) - case "object": - return new(ObjectAttr) - case "component": - return new(ComponentAttr) +func (Spec) NewAttrGroup(parent xml.Name) spec.AttrGroup { + if parent.Space == go3mf.Namespace { + switch parent.Local { + case "build": + return new(BuildAttr) + case "item": + return new(ItemAttr) + case "object": + return new(ObjectAttr) + case "component": + return new(ComponentAttr) + } } return nil } diff --git a/read_test.go b/read_test.go index bfb0ec0..bbac68b 100644 --- a/read_test.go +++ b/read_test.go @@ -42,18 +42,18 @@ var fooSpec = Extension{ type qmExtension struct{} -func (qmExtension) NewAttr3MF(parent string) spec.AttrGroup { +func (qmExtension) NewAttrGroup(xml.Name) spec.AttrGroup { return &fakeAttr{} } -func (qmExtension) NewElementDecoder(parent interface{}, name string) spec.ElementDecoder { +func (qmExtension) NewElementDecoder(parent interface{}, _ string) spec.ElementDecoder { if e, ok := parent.(*Resources); ok { return &fakeAssetDecoder{resources: e} } return nil } -func (qmExtension) Validate(model interface{}, path string, element interface{}) error { +func (qmExtension) Validate(model interface{}, _ string, element interface{}) error { if _, ok := element.(*Model); !ok { return nil } diff --git a/slices/decoder.go b/slices/decoder.go index 4a2f756..1876fc9 100644 --- a/slices/decoder.go +++ b/slices/decoder.go @@ -19,10 +19,12 @@ func (Spec) NewElementDecoder(parent interface{}, name string) spec.ElementDecod return nil } -func (Spec) NewAttr3MF(parentNode string) spec.AttrGroup { - switch parentNode { - case "object": - return new(ObjectAttr) +func (Spec) NewAttrGroup(parent xml.Name) spec.AttrGroup { + if parent.Space == go3mf.Namespace { + switch parent.Local { + case "object": + return new(ObjectAttr) + } } return nil } diff --git a/spec/encoding.go b/spec/encoding.go new file mode 100644 index 0000000..e31d6ee --- /dev/null +++ b/spec/encoding.go @@ -0,0 +1,60 @@ +// © Copyright 2021 HP Development Company, L.P. +// SPDX-License Identifier: BSD-2-Clause + +package spec + +import "encoding/xml" + +// Marshaler is the interface implemented by objects +// that can marshal themselves into valid XML elements. +type Marshaler interface { + Marshal3MF(Encoder, *xml.StartElement) error +} + +// UnmarshalerAttr is the interface implemented by objects that can unmarshal +// an XML element description of themselves. +type UnmarshalerAttr interface { + Unmarshal3MFAttr(XMLAttr) error +} + +// ElementDecoder defines the minimum contract to decode a 3MF node. +type ElementDecoder interface { + Start([]XMLAttr) error + End() +} + +// ChildElementDecoder must be implemented by element decoders +// that need decoding nested elements. +type ChildElementDecoder interface { + ElementDecoder + Child(xml.Name) ElementDecoder +} + +// CharDataElementDecoder must be implemented by element decoders +// that need to decode raw text. +type CharDataElementDecoder interface { + ElementDecoder + CharData([]byte) +} + +// AppendTokenElementDecoder must be implemented by element decoders +// that need to accumulate tokens to support loseless encoding. +type AppendTokenElementDecoder interface { + ElementDecoder + AppendToken(xml.Token) +} + +// Encoder provides de necessary methods to encode specs. +// It should not be implemented by spec authors but +// will be provided be go3mf itself. +type Encoder interface { + AddRelationship(Relationship) + FloatPresicion() int + EncodeToken(xml.Token) + Flush() error + SetAutoClose(bool) + // Use SetSkipAttrEscape(true) when there is no need to escape + // StartElement attribute values, such as as when all attributes + // are filled using strconv. + SetSkipAttrEscape(bool) +} diff --git a/spec/spec.go b/spec/spec.go index c691137..5f3c0eb 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -8,18 +8,46 @@ import ( "sync" ) +var ( + specMu sync.RWMutex + specs = make(map[string]Spec) +) + +// Register makes a spec available by the provided namesoace. +// If Register is called twice with the same name or if spec is nil, +// it panics. +func Register(namespace string, spec Spec) { + specMu.Lock() + defer specMu.Unlock() + specs[namespace] = spec +} + +func Load(space string) (Spec, bool) { + specMu.RLock() + ext, ok := specs[space] + specMu.RUnlock() + return ext, ok +} + +func LoadValidator(ns string) (ValidateSpec, bool) { + specMu.RLock() + ext, ok := specs[ns] + specMu.RUnlock() + if ok { + ext, ok := ext.(ValidateSpec) + return ext, ok + } + return nil, false +} + // Spec is the interface that must be implemented by a 3mf spec. // // Specs may implement ValidateSpec. type Spec interface { - NewAttr3MF(parent string) AttrGroup + NewAttrGroup(parent xml.Name) AttrGroup NewElementDecoder(parent interface{}, name string) ElementDecoder } -type PropertyGroup interface { - Len() int -} - // If a Spec implemented ValidateSpec, then model.Validate will call // Validate and aggregate the resulting erros. // @@ -42,78 +70,10 @@ type Relationship struct { ID string } -// Marshaler is the interface implemented by objects -// that can marshal themselves into valid XML elements. -type Marshaler interface { - Marshal3MF(Encoder, *xml.StartElement) error -} - -// UnmarshalerAttr is the interface implemented by objects that can unmarshal -// an XML element description of themselves. -type UnmarshalerAttr interface { - Unmarshal3MFAttr(XMLAttr) error -} - type ErrorWrapper interface { Wrap(error) error } -// ElementDecoder defines the minimum contract to decode a 3MF node. -type ElementDecoder interface { - Start([]XMLAttr) error - End() -} - -// ChildElementDecoder must be implemented by element decoders -// that need decoding nested elements. -type ChildElementDecoder interface { - ElementDecoder - Child(xml.Name) ElementDecoder -} - -// CharDataElementDecoder must be implemented by element decoders -// that need to decode raw text. -type CharDataElementDecoder interface { - ElementDecoder - CharData([]byte) -} - -// AppendTokenElementDecoder must be implemented by element decoders -// that need to accumulate tokens to support loseless encoding. -type AppendTokenElementDecoder interface { - ElementDecoder - AppendToken(xml.Token) -} - -// Encoder provides de necessary methods to encode specs. -// It should not be implemented by spec authors but -// will be provided be go3mf itself. -type Encoder interface { - AddRelationship(Relationship) - FloatPresicion() int - EncodeToken(xml.Token) - Flush() error - SetAutoClose(bool) - // Use SetSkipAttrEscape(true) when there is no need to escape - // StartElement attribute values, such as as when all attributes - // are filled using strconv. - SetSkipAttrEscape(bool) -} - -var ( - specMu sync.RWMutex - specs = make(map[string]Spec) -) - -// Register makes a spec available by the provided namesoace. -// If Register is called twice with the same name or if spec is nil, -// it panics. -func Register(namespace string, spec Spec) { - specMu.Lock() - defer specMu.Unlock() - specs[namespace] = spec -} - // AttrGroup defines a container for different attributes of the same namespace. // It supports encoding and decoding to XML. type AttrGroup interface { @@ -122,6 +82,10 @@ type AttrGroup interface { Namespace() string } +type PropertyGroup interface { + Len() int +} + type AnyAttr []AttrGroup func (a AnyAttr) Get(namespace string) AttrGroup { @@ -143,9 +107,9 @@ func (a AnyAttr) Marshal3MF(x Encoder, start *xml.StartElement) error { return nil } -func NewAttr3MF(namespace, parent string) AttrGroup { - if ext, ok := LoadExtension(namespace); ok { - return ext.NewAttr3MF(parent) +func NewAttrGroup(namespace string, parent xml.Name) AttrGroup { + if ext, ok := Load(namespace); ok { + return ext.NewAttrGroup(parent) } return &UnknownAttrs{ Space: namespace, @@ -163,21 +127,3 @@ func (e Any) Marshal3MF(x Encoder, start *xml.StartElement) error { } return nil } - -func LoadExtension(space string) (Spec, bool) { - specMu.RLock() - ext, ok := specs[space] - specMu.RUnlock() - return ext, ok -} - -func LoadValidator(ns string) (ValidateSpec, bool) { - specMu.RLock() - ext, ok := specs[ns] - specMu.RUnlock() - if ok { - ext, ok := ext.(ValidateSpec) - return ext, ok - } - return nil, false -} diff --git a/validate.go b/validate.go index cdc0705..dcef8d4 100644 --- a/validate.go +++ b/validate.go @@ -306,7 +306,7 @@ func (r *Object) validateComponents(m *Model, path string) error { func (m *Model) validateNamespaces() error { for _, ext := range m.Extensions { if ext.IsRequired { - if _, ok := spec.LoadExtension(ext.Namespace); !ok { + if _, ok := spec.Load(ext.Namespace); !ok { return errors.ErrRequiredExt } } From 256e7544d1a2c3773f04f5d28e18d1cf5b32d84c Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 5 Oct 2021 14:46:52 +0200 Subject: [PATCH 09/23] implement unknowndecoder constructors --- decoder.go | 6 +++--- spec/unknown.go | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/decoder.go b/decoder.go index 93f57a6..d9c7946 100644 --- a/decoder.go +++ b/decoder.go @@ -39,7 +39,7 @@ func (d *modelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { } else if ext, ok := spec.Load(name.Space); ok { child = ext.NewElementDecoder(d.model, name.Local) } else { - child = &spec.AnyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.model.Any} + child = spec.NewAnyUnknownDecoder(name, &d.model.Any) } return } @@ -297,7 +297,7 @@ func (d *resourceDecoder) Child(name xml.Name) (child spec.ElementDecoder) { } else if ext, ok := spec.Load(name.Space); ok { child = ext.NewElementDecoder(d.resources, name.Local) } else { - child = &unknownAssetDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, resources: d.resources} + child = &unknownAssetDecoder{UnknownTokensDecoder: *spec.NewUnknownDecoder(name), resources: d.resources} } return } @@ -425,7 +425,7 @@ func (d *meshDecoder) Child(name xml.Name) (child spec.ElementDecoder) { } else if ext, ok := spec.Load(name.Space); ok { child = ext.NewElementDecoder(d.resource.Mesh, name.Local) } else { - child = &spec.AnyUnknownDecoder{UnknownTokensDecoder: spec.UnknownTokensDecoder{Name: name}, Any: &d.resource.Mesh.Any} + child = spec.NewAnyUnknownDecoder(name, &d.resource.Mesh.Any) } return } diff --git a/spec/unknown.go b/spec/unknown.go index 7db29df..6bda1b1 100644 --- a/spec/unknown.go +++ b/spec/unknown.go @@ -45,6 +45,12 @@ type UnknownTokensDecoder struct { tokens UnknownTokens } +func NewUnknownDecoder(name xml.Name) *UnknownTokensDecoder { + return &UnknownTokensDecoder{ + Name: name, + } +} + func (d *UnknownTokensDecoder) Start(attrs []XMLAttr) error { var xattrs []xml.Attr if len(attrs) > 0 { @@ -77,6 +83,15 @@ type AnyUnknownDecoder struct { Any *Any } +func NewAnyUnknownDecoder(name xml.Name, any *Any) *AnyUnknownDecoder { + return &AnyUnknownDecoder{ + UnknownTokensDecoder: UnknownTokensDecoder{ + Name: name, + }, + Any: any, + } +} + func (d *AnyUnknownDecoder) End() { d.UnknownTokensDecoder.End() *d.Any = append(*d.Any, d.Tokens()) From b4ee74b3943ad913804e0363a5bdfa801dd77358 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 5 Oct 2021 15:17:43 +0200 Subject: [PATCH 10/23] don't reuse wrap method --- decoder.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/decoder.go b/decoder.go index d9c7946..7fda7e9 100644 --- a/decoder.go +++ b/decoder.go @@ -193,7 +193,7 @@ func (d *buildDecoder) Start(attrs []spec.XMLAttr) error { errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } if errs != nil { - return d.Wrap(errs) + return specerr.Wrap(errs, d.build) } return errs } @@ -686,7 +686,7 @@ func (d *componentsDecoder) Start(attrs []spec.XMLAttr) error { } d.resource.Components = components if errs != nil { - return d.Wrap(errs) + return specerr.Wrap(errs, d.resource.Components) } return nil } From 0da0e1f71e2ab6c5872e88e0caa369efd63a01a7 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 5 Oct 2021 16:08:20 +0200 Subject: [PATCH 11/23] improve beamlattice error handling --- beamlattice/beamlattice.go | 12 +++++-- beamlattice/beamlattice_test.go | 4 +++ beamlattice/decoder.go | 63 ++++++++++++++++++--------------- beamlattice/decoder_test.go | 14 ++++---- beamlattice/encoder.go | 4 +-- beamlattice/encoder_test.go | 4 +-- beamlattice/validate.go | 4 +-- beamlattice/validate_test.go | 8 ++--- 8 files changed, 66 insertions(+), 47 deletions(-) diff --git a/beamlattice/beamlattice.go b/beamlattice/beamlattice.go index 6bbabf5..9375659 100644 --- a/beamlattice/beamlattice.go +++ b/beamlattice/beamlattice.go @@ -92,12 +92,20 @@ type BeamLattice struct { ClipMode ClipMode ClippingMeshID uint32 RepresentationMeshID uint32 - Beams []Beam - BeamSets []BeamSet + Beams Beams + BeamSets BeamSets MinLength, Radius float32 CapMode CapMode } +type Beams struct { + Beam []Beam +} + +type BeamSets struct { + BeamSet []BeamSet +} + func GetBeamLattice(mesh *go3mf.Mesh) *BeamLattice { for _, a := range mesh.Any { if a, ok := a.(*BeamLattice); ok { diff --git a/beamlattice/beamlattice_test.go b/beamlattice/beamlattice_test.go index 8aa6e9b..1d1f1c4 100644 --- a/beamlattice/beamlattice_test.go +++ b/beamlattice/beamlattice_test.go @@ -11,6 +11,10 @@ import ( ) var _ spec.Marshaler = new(BeamLattice) +var _ spec.ChildElementDecoder = new(beamLatticeDecoder) +var _ spec.ChildElementDecoder = new(beamsDecoder) +var _ spec.ChildElementDecoder = new(beamSetsDecoder) +var _ spec.ChildElementDecoder = new(beamSetDecoder) func TestCapMode_String(t *testing.T) { tests := []struct { diff --git a/beamlattice/decoder.go b/beamlattice/decoder.go index 385a165..5e7409c 100644 --- a/beamlattice/decoder.go +++ b/beamlattice/decoder.go @@ -25,13 +25,14 @@ func (Spec) NewElementDecoder(parent interface{}, name string) spec.ElementDecod type beamLatticeDecoder struct { baseDecoder - mesh *go3mf.Mesh + mesh *go3mf.Mesh + beamLattice *BeamLattice } func (d *beamLatticeDecoder) Start(attrs []spec.XMLAttr) error { var errs error - beamLattice := new(BeamLattice) - d.mesh.Any = append(d.mesh.Any, beamLattice) + d.beamLattice = new(BeamLattice) + d.mesh.Any = append(d.mesh.Any, d.beamLattice) for _, a := range attrs { if a.Name.Space != "" { continue @@ -42,16 +43,16 @@ func (d *beamLatticeDecoder) Start(attrs []spec.XMLAttr) error { if err != nil { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, true)) } - beamLattice.Radius = float32(val) + d.beamLattice.Radius = float32(val) case attrMinLength, attrPrecision: // lib3mf legacy val, err := strconv.ParseFloat(string(a.Value), 32) if err != nil { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, true)) } - beamLattice.MinLength = float32(val) + d.beamLattice.MinLength = float32(val) case attrClippingMode, attrClipping: // lib3mf legacy var ok bool - beamLattice.ClipMode, ok = newClipMode(string(a.Value)) + d.beamLattice.ClipMode, ok = newClipMode(string(a.Value)) if !ok { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, false)) } @@ -60,37 +61,37 @@ func (d *beamLatticeDecoder) Start(attrs []spec.XMLAttr) error { if err != nil { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, false)) } - beamLattice.ClippingMeshID = uint32(val) + d.beamLattice.ClippingMeshID = uint32(val) case attrRepresentationMesh: val, err := strconv.ParseUint(string(a.Value), 10, 32) if err != nil { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, false)) } - beamLattice.RepresentationMeshID = uint32(val) + d.beamLattice.RepresentationMeshID = uint32(val) case attrCap: var ok bool - beamLattice.CapMode, ok = newCapMode(string(a.Value)) + d.beamLattice.CapMode, ok = newCapMode(string(a.Value)) if !ok { errs = specerr.Append(errs, specerr.NewParseAttrError(a.Name.Local, false)) } } } if errs != nil { - return specerr.Wrap(errs, GetBeamLattice(d.mesh)) + return specerr.Wrap(errs, d.beamLattice) } return nil } func (d *beamLatticeDecoder) Wrap(err error) error { - return specerr.Wrap(err, GetBeamLattice(d.mesh)) + return specerr.Wrap(err, d.beamLattice) } func (d *beamLatticeDecoder) Child(name xml.Name) (child spec.ElementDecoder) { if name.Space == Namespace { if name.Local == attrBeams { - child = &beamsDecoder{mesh: d.mesh} + child = &beamsDecoder{beamLattice: d.beamLattice} } else if name.Local == attrBeamSets { - child = &beamSetsDecoder{mesh: d.mesh} + child = &beamSetsDecoder{beamLattice: d.beamLattice} } } return @@ -98,12 +99,12 @@ func (d *beamLatticeDecoder) Child(name xml.Name) (child spec.ElementDecoder) { type beamsDecoder struct { baseDecoder - mesh *go3mf.Mesh + beamLattice *BeamLattice beamDecoder beamDecoder } func (d *beamsDecoder) Start(_ []spec.XMLAttr) error { - d.beamDecoder.mesh = d.mesh + d.beamDecoder.beamLattice = d.beamLattice return nil } @@ -114,9 +115,13 @@ func (d *beamsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } +func (d *beamsDecoder) Wrap(err error) error { + return specerr.Wrap(err, &d.beamLattice.Beams) +} + type beamDecoder struct { baseDecoder - mesh *go3mf.Mesh + beamLattice *BeamLattice } func (d *beamDecoder) Start(attrs []spec.XMLAttr) error { @@ -125,7 +130,6 @@ func (d *beamDecoder) Start(attrs []spec.XMLAttr) error { hasCap1, hasCap2 bool errs error ) - beamLattice := GetBeamLattice(d.mesh) for _, a := range attrs { if a.Name.Space != "" { continue @@ -170,46 +174,49 @@ func (d *beamDecoder) Start(attrs []spec.XMLAttr) error { } } if beam.Radius[0] == 0 { - beam.Radius[0] = beamLattice.Radius + beam.Radius[0] = d.beamLattice.Radius } if beam.Radius[1] == 0 { beam.Radius[1] = beam.Radius[0] } if !hasCap1 { - beam.CapMode[0] = beamLattice.CapMode + beam.CapMode[0] = d.beamLattice.CapMode } if !hasCap2 { - beam.CapMode[1] = beamLattice.CapMode + beam.CapMode[1] = d.beamLattice.CapMode } - beamLattice.Beams = append(beamLattice.Beams, beam) + d.beamLattice.Beams.Beam = append(d.beamLattice.Beams.Beam, beam) if errs != nil { - return specerr.WrapIndex(errs, beam, len(beamLattice.Beams)-1) + return specerr.WrapIndex(errs, beam, len(d.beamLattice.Beams.Beam)-1) } return nil } type beamSetsDecoder struct { baseDecoder - mesh *go3mf.Mesh + beamLattice *BeamLattice } func (d *beamSetsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrBeamSet { - child = &beamSetDecoder{mesh: d.mesh} + child = &beamSetDecoder{beamLattice: d.beamLattice} } return } +func (d *beamSetsDecoder) Wrap(err error) error { + return specerr.Wrap(err, &d.beamLattice.BeamSets) +} + type beamSetDecoder struct { baseDecoder - mesh *go3mf.Mesh + beamLattice *BeamLattice beamSet BeamSet beamRefDecoder beamRefDecoder } func (d *beamSetDecoder) End() { - beamLattice := GetBeamLattice(d.mesh) - beamLattice.BeamSets = append(beamLattice.BeamSets, d.beamSet) + d.beamLattice.BeamSets.BeamSet = append(d.beamLattice.BeamSets.BeamSet, d.beamSet) } func (d *beamSetDecoder) Start(attrs []spec.XMLAttr) error { @@ -229,7 +236,7 @@ func (d *beamSetDecoder) Start(attrs []spec.XMLAttr) error { } func (d *beamSetDecoder) Wrap(err error) error { - return specerr.WrapIndex(err, &d.beamSet, len(GetBeamLattice(d.mesh).BeamSets)) + return specerr.WrapIndex(err, &d.beamSet, len(d.beamLattice.BeamSets.BeamSet)) } func (d *beamSetDecoder) Child(name xml.Name) (child spec.ElementDecoder) { diff --git a/beamlattice/decoder_test.go b/beamlattice/decoder_test.go index ce337c0..c97a64d 100644 --- a/beamlattice/decoder_test.go +++ b/beamlattice/decoder_test.go @@ -34,8 +34,8 @@ func TestDecode(t *testing.T) { {55, 45, 55}, {55, 45, 45}, }...) - beamLattice.BeamSets = append(beamLattice.BeamSets, BeamSet{Name: "test", Identifier: "set_id", Refs: []uint32{1}}) - beamLattice.Beams = append(beamLattice.Beams, []Beam{ + beamLattice.BeamSets.BeamSet = append(beamLattice.BeamSets.BeamSet, BeamSet{Name: "test", Identifier: "set_id", Refs: []uint32{1}}) + beamLattice.Beams.Beam = append(beamLattice.Beams.Beam, []Beam{ {Indices: [2]uint32{0, 1}, Radius: [2]float32{1.5, 1.6}, CapMode: [2]CapMode{CapModeSphere, CapModeButt}}, {Indices: [2]uint32{2, 0}, Radius: [2]float32{3, 1.5}, CapMode: [2]CapMode{CapModeSphere, CapModeHemisphere}}, {Indices: [2]uint32{1, 3}, Radius: [2]float32{1.6, 3}, CapMode: [2]CapMode{CapModeHemisphere, CapModeHemisphere}}, @@ -125,11 +125,11 @@ func TestDecode_warns(t *testing.T) { fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", errors.NewParseAttrError("clippingmode", false)), fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", errors.NewParseAttrError("clippingmesh", false)), fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", errors.NewParseAttrError("representationmesh", false)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#0: %v", errors.NewParseAttrError("r1", false)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#0: %v", errors.NewParseAttrError("r2", false)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#2: %v", errors.NewParseAttrError("v2", true)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#3: %v", errors.NewParseAttrError("v1", true)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@BeamSet#0@uint32#2: %v", errors.NewParseAttrError("index", true)), + fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beams@Beam#0: %v", errors.NewParseAttrError("r1", false)), + fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beams@Beam#0: %v", errors.NewParseAttrError("r2", false)), + fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beams@Beam#2: %v", errors.NewParseAttrError("v2", true)), + fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beams@Beam#3: %v", errors.NewParseAttrError("v1", true)), + fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@BeamSets@BeamSet#0@uint32#2: %v", errors.NewParseAttrError("index", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/beamlattice/encoder.go b/beamlattice/encoder.go index e4399e1..67ebf58 100644 --- a/beamlattice/encoder.go +++ b/beamlattice/encoder.go @@ -46,7 +46,7 @@ func (m *BeamLattice) Marshal3MF(x spec.Encoder, _ *xml.StartElement) error { func marshalBeamsets(x spec.Encoder, m *BeamLattice) { xb := xml.StartElement{Name: xml.Name{Space: Namespace, Local: attrBeamSets}} x.EncodeToken(xb) - for _, bs := range m.BeamSets { + for _, bs := range m.BeamSets.BeamSet { xbs := xml.StartElement{Name: xml.Name{Space: Namespace, Local: attrBeamSet}} if bs.Name != "" { xbs.Attr = append(xbs.Attr, xml.Attr{Name: xml.Name{Local: attrName}, Value: bs.Name}) @@ -72,7 +72,7 @@ func marshalBeams(x spec.Encoder, m *BeamLattice) { x.EncodeToken(xb) x.SetAutoClose(true) x.SetSkipAttrEscape(true) - for _, b := range m.Beams { + for _, b := range m.Beams.Beam { xbeam := xml.StartElement{Name: xml.Name{Space: Namespace, Local: attrBeam}, Attr: []xml.Attr{ {Name: xml.Name{Local: attrV1}, Value: strconv.FormatUint(uint64(b.Indices[0]), 10)}, {Name: xml.Name{Local: attrV2}, Value: strconv.FormatUint(uint64(b.Indices[1]), 10)}, diff --git a/beamlattice/encoder_test.go b/beamlattice/encoder_test.go index 9ba8f60..4e61fb5 100644 --- a/beamlattice/encoder_test.go +++ b/beamlattice/encoder_test.go @@ -32,8 +32,8 @@ func TestMarshalModel(t *testing.T) { {55, 45, 55}, {55, 45, 45}, }...) - beamLattice.BeamSets = append(beamLattice.BeamSets, BeamSet{Name: "test", Identifier: "set_id", Refs: []uint32{1}}) - beamLattice.Beams = append(beamLattice.Beams, []Beam{ + beamLattice.BeamSets.BeamSet = append(beamLattice.BeamSets.BeamSet, BeamSet{Name: "test", Identifier: "set_id", Refs: []uint32{1}}) + beamLattice.Beams.Beam = append(beamLattice.Beams.Beam, []Beam{ {Indices: [2]uint32{0, 1}, Radius: [2]float32{1.5, 1.6}, CapMode: [2]CapMode{CapModeSphere, CapModeButt}}, {Indices: [2]uint32{2, 0}, Radius: [2]float32{3, 1.5}, CapMode: [2]CapMode{CapModeSphere, CapModeHemisphere}}, {Indices: [2]uint32{1, 3}, Radius: [2]float32{1.6, 3}, CapMode: [2]CapMode{CapModeHemisphere, CapModeHemisphere}}, diff --git a/beamlattice/validate.go b/beamlattice/validate.go index c0e3924..eb0459d 100644 --- a/beamlattice/validate.go +++ b/beamlattice/validate.go @@ -46,7 +46,7 @@ func validateObject(m *go3mf.Model, path string, obj *go3mf.Object) error { errs = errors.Append(errs, validateRefMesh(m, path, bl.RepresentationMeshID, obj.ID)) } - for i, b := range bl.Beams { + for i, b := range bl.Beams.Beam { if b.Indices[0] == b.Indices[1] { errs = errors.Append(errs, errors.WrapIndex(ErrLatticeSameVertex, b, i)) } else { @@ -59,7 +59,7 @@ func validateObject(m *go3mf.Model, path string, obj *go3mf.Object) error { errs = errors.Append(errs, errors.WrapIndex(ErrLatticeBeamR2, b, i)) } } - for i, set := range bl.BeamSets { + for i, set := range bl.BeamSets.BeamSet { for _, ref := range set.Refs { if int(ref) >= len(set.Refs) { errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, set, i)) diff --git a/beamlattice/validate_test.go b/beamlattice/validate_test.go index 225b8c1..6fdecb0 100644 --- a/beamlattice/validate_test.go +++ b/beamlattice/validate_test.go @@ -70,10 +70,10 @@ func TestValidate(t *testing.T) { }}, {"incorrect beams", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ - MinLength: 1, Radius: 1, ClipMode: ClipInside, Beams: []Beam{ + MinLength: 1, Radius: 1, ClipMode: ClipInside, Beams: Beams{Beam: []Beam{ {}, {Indices: [2]uint32{1, 1}, Radius: [2]float32{0.5, 0}}, {Indices: [2]uint32{1, 3}}, }, - }}}}, + }}}}}, }}}, []string{ fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#0: %v", ErrLatticeSameVertex), fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#1: %v", ErrLatticeSameVertex), @@ -82,9 +82,9 @@ func TestValidate(t *testing.T) { }}, {"incorrect beamseat", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ - MinLength: 1, Radius: 1, ClipMode: ClipInside, Beams: []Beam{ + MinLength: 1, Radius: 1, ClipMode: ClipInside, Beams: Beams{Beam: []Beam{ {Indices: [2]uint32{1, 2}}, - }, BeamSets: []BeamSet{{Refs: []uint32{0, 2, 3}}}, + }}, BeamSets: BeamSets{BeamSet: []BeamSet{{Refs: []uint32{0, 2, 3}}}}, }}}}, }}}, []string{ fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@BeamSet#0: %v", errors.ErrIndexOutOfBounds), From 8fc350ddba79b178a0aae1290054042c317d071a Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 5 Oct 2021 16:16:29 +0200 Subject: [PATCH 12/23] refactor slices --- slices/decoder.go | 10 +++++++--- slices/decoder_test.go | 10 +++++----- slices/encoder.go | 2 +- slices/encoder_test.go | 6 +++--- slices/slices.go | 8 ++++++-- slices/validate.go | 4 ++-- slices/validate_test.go | 20 ++++++++++---------- 7 files changed, 34 insertions(+), 26 deletions(-) diff --git a/slices/decoder.go b/slices/decoder.go index 1876fc9..931aeaa 100644 --- a/slices/decoder.go +++ b/slices/decoder.go @@ -136,7 +136,7 @@ type sliceDecoder struct { } func (d *sliceDecoder) End() { - d.resource.Slices = append(d.resource.Slices, &d.slice) + d.resource.Slices = append(d.resource.Slices, d.slice) } func (d *sliceDecoder) Wrap(err error) error { @@ -192,6 +192,10 @@ func (d *polygonVerticesDecoder) Child(name xml.Name) (child spec.ElementDecoder return } +func (d *polygonVerticesDecoder) Wrap(err error) error { + return specerr.Wrap(err, &d.slice.Vertices) +} + type polygonVertexDecoder struct { baseDecoder slice *Slice @@ -214,9 +218,9 @@ func (d *polygonVertexDecoder) Start(attrs []spec.XMLAttr) error { p[1] = float32(val) } } - d.slice.Vertices = append(d.slice.Vertices, p) + d.slice.Vertices.Vertex = append(d.slice.Vertices.Vertex, p) if errs != nil { - return specerr.WrapIndex(errs, p, len(d.slice.Vertices)-1) + return specerr.WrapIndex(errs, p, len(d.slice.Vertices.Vertex)-1) } return nil } diff --git a/slices/decoder_test.go b/slices/decoder_test.go index 8a3f865..10b50ad 100644 --- a/slices/decoder_test.go +++ b/slices/decoder_test.go @@ -15,15 +15,15 @@ import ( func TestDecode(t *testing.T) { sliceStack := &SliceStack{ID: 3, BottomZ: 1, - Slices: []*Slice{ + Slices: []Slice{ { TopZ: 0, - Vertices: []go3mf.Point2D{{1.01, 1.02}, {9.03, 1.04}, {9.05, 9.06}, {1.07, 9.08}}, + Vertices: Vertices{Vertex: []go3mf.Point2D{{1.01, 1.02}, {9.03, 1.04}, {9.05, 9.06}, {1.07, 9.08}}}, Polygons: []Polygon{{StartV: 0, Segments: []Segment{{V2: 1, PID: 1, P1: 2, P2: 3}, {V2: 2, PID: 1, P1: 2, P2: 2}, {V2: 3}, {V2: 0}}}}, }, { TopZ: 0.1, - Vertices: []go3mf.Point2D{{1.01, 1.02}, {9.03, 1.04}, {9.05, 9.06}, {1.07, 9.08}}, + Vertices: Vertices{Vertex: []go3mf.Point2D{{1.01, 1.02}, {9.03, 1.04}, {9.05, 9.06}, {1.07, 9.08}}}, Polygons: []Polygon{{StartV: 0, Segments: []Segment{{V2: 2}, {V2: 1}, {V2: 3}, {V2: 0}}}}, }, }, @@ -97,8 +97,8 @@ func TestDecode_warns(t *testing.T) { want := []string{ fmt.Sprintf("Resources@SliceStack#0: %v", specerr.NewParseAttrError("id", true)), fmt.Sprintf("Resources@SliceStack#0: %v", specerr.NewParseAttrError("zbottom", false)), - fmt.Sprintf("Resources@SliceStack#0@Slice#0@Point2D#0: %v", specerr.NewParseAttrError("x", true)), - fmt.Sprintf("Resources@SliceStack#0@Slice#0@Point2D#1: %v", specerr.NewParseAttrError("y", true)), + fmt.Sprintf("Resources@SliceStack#0@Slice#0@Vertices@Point2D#0: %v", specerr.NewParseAttrError("x", true)), + fmt.Sprintf("Resources@SliceStack#0@Slice#0@Vertices@Point2D#1: %v", specerr.NewParseAttrError("y", true)), fmt.Sprintf("Resources@SliceStack#0@Slice#1: %v", specerr.NewParseAttrError("ztop", true)), fmt.Sprintf("Resources@SliceStack#0@Slice#1@Polygon#0: %v", specerr.NewParseAttrError("startv", true)), fmt.Sprintf("Resources@SliceStack#0@Slice#1@Polygon#0@Segment#1: %v", specerr.NewParseAttrError("v2", true)), diff --git a/slices/encoder.go b/slices/encoder.go index 9093aad..20d2cef 100644 --- a/slices/encoder.go +++ b/slices/encoder.go @@ -53,7 +53,7 @@ func (s *Slice) marshal3MF(x spec.Encoder) { }} x.EncodeToken(xs) - marshalVertices(x, s.Vertices) + marshalVertices(x, s.Vertices.Vertex) marshalPolygons(x, s.Polygons) x.EncodeToken(xs.End()) diff --git a/slices/encoder_test.go b/slices/encoder_test.go index d7aabcd..790e38e 100644 --- a/slices/encoder_test.go +++ b/slices/encoder_test.go @@ -14,15 +14,15 @@ import ( func TestMarshalModel(t *testing.T) { sliceStack := &SliceStack{ID: 3, BottomZ: 1, - Slices: []*Slice{ + Slices: []Slice{ { TopZ: 0, - Vertices: []go3mf.Point2D{{1.01, 1.02}, {9.03, 1.04}, {9.05, 9.06}, {1.07, 9.08}}, + Vertices: Vertices{Vertex: []go3mf.Point2D{{1.01, 1.02}, {9.03, 1.04}, {9.05, 9.06}, {1.07, 9.08}}}, Polygons: []Polygon{{StartV: 0, Segments: []Segment{{V2: 1, PID: 10}, {V2: 2, PID: 10, P2: 1}, {V2: 3}, {V2: 0}}}}, }, { TopZ: 0.1, - Vertices: []go3mf.Point2D{{1.01, 1.02}, {9.03, 1.04}, {9.05, 9.06}, {1.07, 9.08}}, + Vertices: Vertices{Vertex: []go3mf.Point2D{{1.01, 1.02}, {9.03, 1.04}, {9.05, 9.06}, {1.07, 9.08}}}, Polygons: []Polygon{{StartV: 1, Segments: []Segment{{V2: 2}, {V2: 1}, {V2: 3}, {V2: 0}}}}, }, }, diff --git a/slices/slices.go b/slices/slices.go index 53fe980..d681e40 100644 --- a/slices/slices.go +++ b/slices/slices.go @@ -61,10 +61,14 @@ type Polygon struct { // Slice defines the resource object for slices. type Slice struct { TopZ float32 - Vertices []go3mf.Point2D + Vertices Vertices Polygons []Polygon } +type Vertices struct { + Vertex []go3mf.Point2D +} + // MeshResolution defines the resolutions for a slice. type MeshResolution uint8 @@ -100,7 +104,7 @@ type SliceRef struct { type SliceStack struct { ID uint32 BottomZ float32 - Slices []*Slice + Slices []Slice Refs []SliceRef } diff --git a/slices/validate.go b/slices/validate.go index 02765b6..5d098bc 100644 --- a/slices/validate.go +++ b/slices/validate.go @@ -95,10 +95,10 @@ func (r *SliceStack) validateSlices() error { errs = errors.Append(errs, errors.WrapIndex(ErrSliceNoMonotonic, slice, j)) } lastTopZ = slice.TopZ - if len(slice.Polygons) == 0 && len(slice.Vertices) == 0 { + if len(slice.Polygons) == 0 && len(slice.Vertices.Vertex) == 0 { continue } - if len(slice.Vertices) < 2 { + if len(slice.Vertices.Vertex) < 2 { errs = errors.Append(errs, errors.WrapIndex(ErrSliceInsufficientVertices, slice, j)) } if len(slice.Polygons) == 0 { diff --git a/slices/validate_test.go b/slices/validate_test.go index ad57452..14627df 100644 --- a/slices/validate_test.go +++ b/slices/validate_test.go @@ -25,7 +25,7 @@ func TestValidate(t *testing.T) { AnyAttr: spec.AnyAttr{&ObjectAttr{SliceStackID: 10}}, Extensions: []go3mf.Extension{{Namespace: Namespace, LocalName: "s", IsRequired: true}}, Resources: go3mf.Resources{ Assets: []go3mf.Asset{ - &SliceStack{ID: 1, Slices: []*Slice{{TopZ: 1}}}, + &SliceStack{ID: 1, Slices: []Slice{{TopZ: 1}}}, }, Objects: []*go3mf.Object{ {ID: 2, AnyAttr: spec.AnyAttr{&ObjectAttr{ @@ -48,10 +48,10 @@ func TestValidate(t *testing.T) { }}, {"slicestack", &go3mf.Model{Resources: go3mf.Resources{ Assets: []go3mf.Asset{&SliceStack{ - ID: 1, BottomZ: 1, Slices: []*Slice{ + ID: 1, BottomZ: 1, Slices: []Slice{ {}, - {TopZ: 0.5, Vertices: make([]go3mf.Point2D, 1)}, - {TopZ: 1.5, Vertices: make([]go3mf.Point2D, 2), Polygons: []Polygon{ + {TopZ: 0.5, Vertices: Vertices{Vertex: make([]go3mf.Point2D, 1)}}, + {TopZ: 1.5, Vertices: Vertices{Vertex: make([]go3mf.Point2D, 2)}, Polygons: []Polygon{ {Segments: []Segment{}}, {Segments: []Segment{{}}}, }}, @@ -71,15 +71,15 @@ func TestValidate(t *testing.T) { {"sliceref", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{ "/that.model": {Resources: go3mf.Resources{Assets: []go3mf.Asset{ - &SliceStack{ID: 1, Slices: []*Slice{{TopZ: 1}, {TopZ: 2}}}, + &SliceStack{ID: 1, Slices: []Slice{{TopZ: 1}, {TopZ: 2}}}, &SliceStack{ID: 2, Refs: []SliceRef{{SliceStackID: 1, Path: rootPath}}}, &go3mf.BaseMaterials{ID: 3, Materials: []go3mf.Base{{Name: "a", Color: color.RGBA{R: 1}}}}, - &SliceStack{ID: 4, Slices: []*Slice{{TopZ: 1.5}}}, + &SliceStack{ID: 4, Slices: []Slice{{TopZ: 1.5}}}, }}}, }, Resources: go3mf.Resources{ Assets: []go3mf.Asset{ - &SliceStack{ID: 1, Slices: []*Slice{{TopZ: 1}, {TopZ: 2}}}, + &SliceStack{ID: 1, Slices: []Slice{{TopZ: 1}, {TopZ: 2}}}, &SliceStack{ID: 3, Refs: []SliceRef{ {}, {SliceStackID: 1, Path: rootPath}, @@ -110,18 +110,18 @@ func TestValidate(t *testing.T) { }}, Childs: map[string]*go3mf.ChildModel{ "/that.model": {Resources: go3mf.Resources{Assets: []go3mf.Asset{ - &SliceStack{ID: 1, Slices: []*Slice{{TopZ: 1, Vertices: []go3mf.Point2D{{}, {}, {}}, Polygons: []Polygon{ + &SliceStack{ID: 1, Slices: []Slice{{TopZ: 1, Vertices: Vertices{Vertex: []go3mf.Point2D{{}, {}, {}}}, Polygons: []Polygon{ {StartV: 1, Segments: []Segment{{V2: 2}}}, }}}}, }}}, }, Resources: go3mf.Resources{ Assets: []go3mf.Asset{ - &SliceStack{ID: 3, Slices: []*Slice{{TopZ: 1, Vertices: []go3mf.Point2D{{}, {}, {}}, Polygons: []Polygon{ + &SliceStack{ID: 3, Slices: []Slice{{TopZ: 1, Vertices: Vertices{Vertex: []go3mf.Point2D{{}, {}, {}}}, Polygons: []Polygon{ {StartV: 1, Segments: []Segment{{V2: 2}}}, }}}}, &go3mf.BaseMaterials{ID: 6, Materials: []go3mf.Base{{Name: "a", Color: color.RGBA{R: 1}}}}, &SliceStack{ID: 9, Refs: []SliceRef{{SliceStackID: 1, Path: "/that.model"}}}, - &SliceStack{ID: 11, Slices: []*Slice{{TopZ: 1, Vertices: []go3mf.Point2D{{}, {}, {}}, Polygons: []Polygon{ + &SliceStack{ID: 11, Slices: []Slice{{TopZ: 1, Vertices: Vertices{Vertex: []go3mf.Point2D{{}, {}, {}}}, Polygons: []Polygon{ {StartV: 1, Segments: []Segment{{V2: 2}, {V2: 1}}}, }}}}, }, From 41996ffeb9ab3e96eee79733a86763381dc65387 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 5 Oct 2021 16:46:51 +0200 Subject: [PATCH 13/23] move vertices and triangles to a container --- beamlattice/decoder_test.go | 2 +- beamlattice/encoder_test.go | 4 +-- beamlattice/validate.go | 2 +- beamlattice/validate_test.go | 10 +++--- core.go | 22 +++++++++---- core_test.go | 8 ++--- decoder.go | 48 +++++++++++++++++++++++----- encoder.go | 6 ++-- encoder_test.go | 8 ++--- importer/stl/ascii.go | 4 +-- importer/stl/binary.go | 6 ++-- importer/stl/binary_test.go | 2 +- production/validate_test.go | 4 +-- read_test.go | 62 ++++++++++++++++++------------------ validate.go | 16 +++++----- validate_test.go | 40 +++++++++++------------ 16 files changed, 143 insertions(+), 101 deletions(-) diff --git a/beamlattice/decoder_test.go b/beamlattice/decoder_test.go index c97a64d..39b828c 100644 --- a/beamlattice/decoder_test.go +++ b/beamlattice/decoder_test.go @@ -24,7 +24,7 @@ func TestDecode(t *testing.T) { beamLattice.MinLength = 0.0001 beamLattice.CapMode = CapModeHemisphere beamLattice.Radius = 1 - meshLattice.Mesh.Vertices = append(meshLattice.Mesh.Vertices, []go3mf.Point3D{ + meshLattice.Mesh.Vertices.Vertex = append(meshLattice.Mesh.Vertices.Vertex, []go3mf.Point3D{ {45, 55, 55}, {45, 45, 55}, {45, 55, 45}, diff --git a/beamlattice/encoder_test.go b/beamlattice/encoder_test.go index 4e61fb5..cea3141 100644 --- a/beamlattice/encoder_test.go +++ b/beamlattice/encoder_test.go @@ -16,13 +16,13 @@ func TestMarshalModel(t *testing.T) { meshLattice := &go3mf.Object{ ID: 15, Name: "Box", Mesh: &go3mf.Mesh{ - Triangles: []go3mf.Triangle{}, + Triangles: go3mf.Triangles{Triangle: []go3mf.Triangle{}}, Any: spec.Any{beamLattice}}, } beamLattice.MinLength = 0.0001 beamLattice.CapMode = CapModeHemisphere beamLattice.Radius = 1 - meshLattice.Mesh.Vertices = append(meshLattice.Mesh.Vertices, []go3mf.Point3D{ + meshLattice.Mesh.Vertices.Vertex = append(meshLattice.Mesh.Vertices.Vertex, []go3mf.Point3D{ {45, 55, 55}, {45, 45, 55}, {45, 55, 45}, diff --git a/beamlattice/validate.go b/beamlattice/validate.go index eb0459d..a08d0bd 100644 --- a/beamlattice/validate.go +++ b/beamlattice/validate.go @@ -50,7 +50,7 @@ func validateObject(m *go3mf.Model, path string, obj *go3mf.Object) error { if b.Indices[0] == b.Indices[1] { errs = errors.Append(errs, errors.WrapIndex(ErrLatticeSameVertex, b, i)) } else { - l := len(obj.Mesh.Vertices) + l := len(obj.Mesh.Vertices.Vertex) if int(b.Indices[0]) >= l || int(b.Indices[1]) >= l { errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, b, i)) } diff --git a/beamlattice/validate_test.go b/beamlattice/validate_test.go index 6fdecb0..8470ab9 100644 --- a/beamlattice/validate_test.go +++ b/beamlattice/validate_test.go @@ -56,11 +56,11 @@ func TestValidate(t *testing.T) { fmt.Sprintf("Resources@Object#2@Mesh@BeamLattice: %v", ErrLatticeObjType), }}, {"incorrect mesh references", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ - {ID: 1, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{nil}}}, - {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ + {ID: 1, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{nil}}}, + {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClippingMeshID: 100, RepresentationMeshID: 2, }}}}, - {ID: 3, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ + {ID: 3, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClippingMeshID: 1, RepresentationMeshID: 2, }}}}, }}}, []string{ @@ -69,7 +69,7 @@ func TestValidate(t *testing.T) { fmt.Sprintf("Resources@Object#2@Mesh@BeamLattice: %v", ErrLatticeInvalidMesh), }}, {"incorrect beams", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ - {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ + {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClipMode: ClipInside, Beams: Beams{Beam: []Beam{ {}, {Indices: [2]uint32{1, 1}, Radius: [2]float32{0.5, 0}}, {Indices: [2]uint32{1, 3}}, }, @@ -81,7 +81,7 @@ func TestValidate(t *testing.T) { fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#2: %v", errors.ErrIndexOutOfBounds), }}, {"incorrect beamseat", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ - {ID: 2, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}}, Any: spec.Any{&BeamLattice{ + {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ MinLength: 1, Radius: 1, ClipMode: ClipInside, Beams: Beams{Beam: []Beam{ {Indices: [2]uint32{1, 2}}, }}, BeamSets: BeamSets{BeamSet: []BeamSet{{Refs: []uint32{0, 2, 3}}}}, diff --git a/core.go b/core.go index 728eebc..2b9ad28 100644 --- a/core.go +++ b/core.go @@ -469,19 +469,29 @@ type Triangle struct { // orientation (i.e. the face can look up or look down) and have three nodes. // The orientation is defined by the order of its nodes. type Mesh struct { - Vertices []Point3D - Triangles []Triangle + Vertices Vertices + Triangles Triangles AnyAttr spec.AnyAttr Any spec.Any } +type Vertices struct { + Vertex []Point3D + AnyAttr spec.AnyAttr +} + +type Triangles struct { + Triangle []Triangle + AnyAttr spec.AnyAttr +} + // BoundingBox returns the bounding box of the mesh. func (m *Mesh) BoundingBox() Box { - if len(m.Vertices) == 0 { + if len(m.Vertices.Vertex) == 0 { return Box{} } box := newLimitBox() - for _, v := range m.Vertices { + for _, v := range m.Vertices.Vertex { box = box.extendPoint(v) } return box @@ -515,8 +525,8 @@ func (mb *MeshBuilder) AddVertex(node Point3D) uint32 { return index } } - mb.Mesh.Vertices = append(mb.Mesh.Vertices, node) - index := uint32(len(mb.Mesh.Vertices)) - 1 + mb.Mesh.Vertices.Vertex = append(mb.Mesh.Vertices.Vertex, node) + index := uint32(len(mb.Mesh.Vertices.Vertex)) - 1 if mb.CalculateConnectivity { mb.vectorTree.AddVector(node, index) } diff --git a/core_test.go b/core_test.go index c5c1bbe..55803bd 100644 --- a/core_test.go +++ b/core_test.go @@ -260,7 +260,7 @@ func TestMeshBuilder_AddVertex(t *testing.T) { want uint32 }{ {"existing", existingStruct, args{pos}, 0}, - {"base", &MeshBuilder{Mesh: &Mesh{Vertices: []Point3D{{}}}, CalculateConnectivity: false}, args{pos}, 1}, + {"base", &MeshBuilder{Mesh: &Mesh{Vertices: Vertices{Vertex: []Point3D{{}}}}, CalculateConnectivity: false}, args{pos}, 1}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -444,7 +444,7 @@ func TestMesh_BoundingBox(t *testing.T) { want Box }{ {"empty", new(Mesh), Box{}}, - {"base", &Mesh{Vertices: []Point3D{{1, 1, 1}, {2, 2, 2}, {-1, 0, 3}}}, Box{Min: Point3D{-1, 0, 1}, Max: Point3D{2, 2, 3}}}, + {"base", &Mesh{Vertices: Vertices{Vertex: []Point3D{{1, 1, 1}, {2, 2, 2}, {-1, 0, 3}}}}, Box{Min: Point3D{-1, 0, 1}, Max: Point3D{2, 2, 3}}}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -469,9 +469,7 @@ func TestModel_BoundingBox(t *testing.T) { {ObjectID: 3}, }}, Resources: Resources{Objects: []*Object{ - {ID: 1, Mesh: &Mesh{Vertices: []Point3D{ - {10, 20, 30}, - }}}, + {ID: 1, Mesh: &Mesh{Vertices: Vertices{Vertex: []Point3D{{10, 20, 30}}}}}, {ID: 2, Components: &Components{Component: []*Component{ {ObjectID: 1, Transform: Identity().Translate(100, 100, 100)}, {ObjectID: 10}, diff --git a/decoder.go b/decoder.go index 7fda7e9..6f7d683 100644 --- a/decoder.go +++ b/decoder.go @@ -436,8 +436,20 @@ type verticesDecoder struct { vertexDecoder vertexDecoder } -func (d *verticesDecoder) Start(_ []spec.XMLAttr) error { +func (d *verticesDecoder) Start(attrs []spec.XMLAttr) error { d.vertexDecoder.mesh = d.mesh + var errs error + for _, a := range attrs { + var attr spec.AttrGroup + if attr = d.mesh.Vertices.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrVertices}) + d.mesh.Vertices.AnyAttr = append(d.mesh.Vertices.AnyAttr, attr) + } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) + } + if errs != nil { + return specerr.Wrap(errs, &d.mesh.Vertices) + } return nil } @@ -448,6 +460,10 @@ func (d *verticesDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } +func (d *verticesDecoder) Wrap(err error) error { + return specerr.Wrap(err, &d.mesh.Vertices) +} + type vertexDecoder struct { baseDecoder mesh *Mesh @@ -475,9 +491,9 @@ func (d *vertexDecoder) Start(attrs []spec.XMLAttr) error { z = float32(val) } } - d.mesh.Vertices = append(d.mesh.Vertices, Point3D{x, y, z}) + d.mesh.Vertices.Vertex = append(d.mesh.Vertices.Vertex, Point3D{x, y, z}) if errs != nil { - return specerr.WrapIndex(errs, Point3D{x, y, z}, len(d.mesh.Vertices)-1) + return specerr.WrapIndex(errs, Point3D{x, y, z}, len(d.mesh.Vertices.Vertex)-1) } return nil } @@ -488,13 +504,25 @@ type trianglesDecoder struct { triangleDecoder triangleDecoder } -func (d *trianglesDecoder) Start(_ []spec.XMLAttr) error { +func (d *trianglesDecoder) Start(attrs []spec.XMLAttr) error { d.triangleDecoder.mesh = d.resource.Mesh d.triangleDecoder.defaultPropertyID = d.resource.PID d.triangleDecoder.defaultPropertyIndex = d.resource.PIndex - if len(d.resource.Mesh.Triangles) == 0 && len(d.resource.Mesh.Vertices) > 0 { - d.resource.Mesh.Triangles = make([]Triangle, 0, len(d.resource.Mesh.Vertices)*2) + if len(d.resource.Mesh.Triangles.Triangle) == 0 && len(d.resource.Mesh.Vertices.Vertex) > 0 { + d.resource.Mesh.Triangles.Triangle = make([]Triangle, 0, len(d.resource.Mesh.Vertices.Vertex)*2) + } + var errs error + for _, a := range attrs { + var attr spec.AttrGroup + if attr = d.resource.Mesh.Triangles.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrTriangles}) + d.resource.Mesh.Triangles.AnyAttr = append(d.resource.Mesh.Triangles.AnyAttr, attr) + } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) + } + if errs != nil { + return specerr.Wrap(errs, &d.resource.Mesh.Triangles) } return nil } @@ -506,6 +534,10 @@ func (d *trianglesDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } +func (d *trianglesDecoder) Wrap(err error) error { + return specerr.Wrap(err, &d.resource.Mesh.Triangles) +} + type triangleDecoder struct { baseDecoder mesh *Mesh @@ -567,9 +599,9 @@ func (d *triangleDecoder) Start(attrs []spec.XMLAttr) error { pid = applyDefault(pid, d.defaultPropertyID, hasPID) t.PID = pid t.P1, t.P2, t.P3 = p1, p2, p3 - d.mesh.Triangles = append(d.mesh.Triangles, t) + d.mesh.Triangles.Triangle = append(d.mesh.Triangles.Triangle, t) if errs != nil { - return specerr.WrapIndex(errs, t, len(d.mesh.Triangles)-1) + return specerr.WrapIndex(errs, t, len(d.mesh.Triangles.Triangle)-1) } return nil } diff --git a/encoder.go b/encoder.go index f37640f..c15b6d8 100644 --- a/encoder.go +++ b/encoder.go @@ -429,6 +429,7 @@ func (e *Encoder) writeComponents(x spec.Encoder, comps *Components) { func (e *Encoder) writeVertices(x spec.Encoder, m *Mesh) { xvs := xml.StartElement{Name: xml.Name{Local: attrVertices}} + m.Vertices.AnyAttr.Marshal3MF(x, &xvs) x.EncodeToken(xvs) prec := x.FloatPresicion() start := xml.StartElement{ @@ -441,7 +442,7 @@ func (e *Encoder) writeVertices(x spec.Encoder, m *Mesh) { } x.SetAutoClose(true) x.SetSkipAttrEscape(true) - for _, v := range m.Vertices { + for _, v := range m.Vertices.Vertex { start.Attr[0].Value = strconv.FormatFloat(float64(v.X()), 'f', prec, 32) start.Attr[1].Value = strconv.FormatFloat(float64(v.Y()), 'f', prec, 32) start.Attr[2].Value = strconv.FormatFloat(float64(v.Z()), 'f', prec, 32) @@ -454,6 +455,7 @@ func (e *Encoder) writeVertices(x spec.Encoder, m *Mesh) { func (e *Encoder) writeTriangles(x spec.Encoder, r *Object, m *Mesh) { xvt := xml.StartElement{Name: xml.Name{Local: attrTriangles}} + m.Triangles.AnyAttr.Marshal3MF(x, &xvt) x.EncodeToken(xvt) start := xml.StartElement{ Name: xml.Name{Local: attrTriangle}, @@ -469,7 +471,7 @@ func (e *Encoder) writeTriangles(x spec.Encoder, r *Object, m *Mesh) { } x.SetAutoClose(true) x.SetSkipAttrEscape(true) - for _, t := range m.Triangles { + for _, t := range m.Triangles.Triangle { attrs[0].Value = strconv.FormatUint(uint64(t.V1), 10) attrs[1].Value = strconv.FormatUint(uint64(t.V2), 10) attrs[2].Value = strconv.FormatUint(uint64(t.V3), 10) diff --git a/encoder_test.go b/encoder_test.go index 89f3d5e..50d6cea 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -72,11 +72,11 @@ func TestMarshalModel(t *testing.T) { xml.StartElement{Name: fooName}, xml.EndElement{Name: fooName}, }}, - Vertices: []Point3D{ + Vertices: Vertices{Vertex: []Point3D{ {0, 0, 0}, {100, 0, 0}, {100, 100, 0}, {0, 100, 0}, {0, 0, 100}, {100, 0, 100}, - {100, 100, 100}, {0, 100, 100}}, - Triangles: []Triangle{ + {100, 100, 100}, {0, 100, 100}}}, + Triangles: Triangles{Triangle: []Triangle{ {V1: 3, V2: 2, V3: 1, PID: 5, P1: 0, P2: 0, P3: 0}, {V1: 1, V2: 0, V3: 3, PID: 5, P1: 0, P2: 0, P3: 0}, {V1: 4, V2: 5, V3: 6, PID: 5, P1: 1, P2: 1, P3: 1}, @@ -89,7 +89,7 @@ func TestMarshalModel(t *testing.T) { {V1: 7, V2: 6, V3: 2, PID: 5, P1: 0, P2: 0, P3: 0}, {V1: 3, V2: 0, V3: 4, PID: 5, P1: 0, P2: 0, P3: 0}, {V1: 4, V2: 7, V3: 3, PID: 5, P1: 0, P2: 0, P3: 0}, - }, + }}, }}, { ID: 20, Type: ObjectTypeSupport, diff --git a/importer/stl/ascii.go b/importer/stl/ascii.go index 56c6a40..d73e4c0 100644 --- a/importer/stl/ascii.go +++ b/importer/stl/ascii.go @@ -38,8 +38,8 @@ func (d *asciiDecoder) decode(ctx context.Context, m *go3mf.Mesh) (err error) { if position == 3 { position = 0 - m.Triangles = append(m.Triangles, go3mf.Triangle{V1: nodes[0], V2: nodes[1], V3: nodes[2]}) - if len(m.Triangles) > nextFaceCheck { + m.Triangles.Triangle = append(m.Triangles.Triangle, go3mf.Triangle{V1: nodes[0], V2: nodes[1], V3: nodes[2]}) + if len(m.Triangles.Triangle) > nextFaceCheck { select { case <-ctx.Done(): err = ctx.Err() diff --git a/importer/stl/binary.go b/importer/stl/binary.go index 5450993..dce85a7 100644 --- a/importer/stl/binary.go +++ b/importer/stl/binary.go @@ -35,7 +35,7 @@ func (d *binaryDecoder) decode(ctx context.Context, m *go3mf.Mesh) error { if err != nil { return err } - mb.Mesh.Triangles = make([]go3mf.Triangle, 0, header.FaceCount) + mb.Mesh.Triangles.Triangle = make([]go3mf.Triangle, 0, header.FaceCount) nextFaceCheck := checkEveryFaces var facet binaryFace for nFace := 0; nFace < int(header.FaceCount); nFace++ { @@ -44,7 +44,7 @@ func (d *binaryDecoder) decode(ctx context.Context, m *go3mf.Mesh) error { break } d.decodeFace(&facet, mb) - if len(m.Triangles) > nextFaceCheck { + if len(m.Triangles.Triangle) > nextFaceCheck { select { case <-ctx.Done(): err = ctx.Err() @@ -64,5 +64,5 @@ func (d *binaryDecoder) decodeFace(facet *binaryFace, mb *go3mf.MeshBuilder) { pos := facet.Vertices[nVertex] nodes[nVertex] = mb.AddVertex(go3mf.Point3D{pos[0], pos[1], pos[2]}) } - mb.Mesh.Triangles = append(mb.Mesh.Triangles, go3mf.Triangle{V1: nodes[0], V2: nodes[1], V3: nodes[2]}) + mb.Mesh.Triangles.Triangle = append(mb.Mesh.Triangles.Triangle, go3mf.Triangle{V1: nodes[0], V2: nodes[1], V3: nodes[2]}) } diff --git a/importer/stl/binary_test.go b/importer/stl/binary_test.go index 74d952c..00ff86e 100644 --- a/importer/stl/binary_test.go +++ b/importer/stl/binary_test.go @@ -57,7 +57,7 @@ func createMeshTriangle(id uint32) *go3mf.Object { n4 := mb.AddVertex(go3mf.Point3D{-20.0, 20.0, 0.0}) n5 := mb.AddVertex(go3mf.Point3D{0.0, 0.0019989014, 39.998}) n6 := mb.AddVertex(go3mf.Point3D{20.0, 20.0, 0.0}) - m.Mesh.Triangles = append(m.Mesh.Triangles, + m.Mesh.Triangles.Triangle = append(m.Mesh.Triangles.Triangle, go3mf.Triangle{V1: n1, V2: n2, V3: n3}, go3mf.Triangle{V1: n4, V2: n2, V3: n1}, go3mf.Triangle{V1: n1, V2: n5, V3: n4}, diff --git a/production/validate_test.go b/production/validate_test.go index 209346d..a370486 100644 --- a/production/validate_test.go +++ b/production/validate_test.go @@ -14,9 +14,9 @@ import ( ) func TestValidate(t *testing.T) { - validMesh := &go3mf.Object{ID: 1, Mesh: &go3mf.Mesh{Vertices: []go3mf.Point3D{{}, {}, {}, {}}, Triangles: []go3mf.Triangle{ + validMesh := &go3mf.Object{ID: 1, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}, {}}}, Triangles: go3mf.Triangles{Triangle: []go3mf.Triangle{ {V1: 0, V2: 1, V3: 2}, {V1: 0, V2: 3, V3: 1}, {V1: 0, V2: 2, V3: 3}, {V1: 1, V2: 3, V3: 2}, - }}} + }}}} tests := []struct { name string model *go3mf.Model diff --git a/read_test.go b/read_test.go index bbac68b..3ab12ef 100644 --- a/read_test.go +++ b/read_test.go @@ -330,32 +330,32 @@ func TestDecoder_processRootModel(t *testing.T) { xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "fake"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "fake"}}, }}, + Vertices: Vertices{Vertex: []Point3D{ + {0, 0, 0}, + {100, 0, 0}, + {100, 100, 0}, + {0, 100, 0}, + {0, 0, 100}, + {100, 0, 100}, + {100, 100, 100}, + {0, 100, 100}, + }, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval10"}}}}}, + Triangles: Triangles{Triangle: []Triangle{ + {V1: 3, V2: 2, V3: 1, PID: 5, P1: 0, P2: 0, P3: 0, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "t1"}}}}}, + {V1: 1, V2: 0, V3: 3, PID: 5, P1: 0, P2: 0, P3: 0}, + {V1: 4, V2: 5, V3: 6, PID: 5, P1: 1, P2: 1, P3: 1}, + {V1: 6, V2: 7, V3: 4, PID: 5, P1: 1, P2: 1, P3: 1}, + {V1: 0, V2: 1, V3: 5, PID: 5, P1: 0, P2: 1, P3: 2}, + {V1: 5, V2: 4, V3: 0, PID: 5, P1: 3, P2: 0, P3: 2}, + {V1: 1, V2: 2, V3: 6, PID: 5, P1: 0, P2: 1, P3: 2}, + {V1: 6, V2: 5, V3: 1, PID: 5, P1: 2, P2: 1, P3: 3}, + {V1: 2, V2: 3, V3: 7, PID: 5, P1: 0, P2: 0, P3: 0}, + {V1: 7, V2: 6, V3: 2, PID: 5, P1: 0, P2: 0, P3: 0}, + {V1: 3, V2: 0, V3: 4, PID: 5, P1: 0, P2: 0, P3: 0}, + {V1: 4, V2: 7, V3: 3, PID: 5, P1: 0, P2: 0, P3: 0}, + }, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval11"}}}}}, }, } - meshRes.Mesh.Vertices = append(meshRes.Mesh.Vertices, []Point3D{ - {0, 0, 0}, - {100, 0, 0}, - {100, 100, 0}, - {0, 100, 0}, - {0, 0, 100}, - {100, 0, 100}, - {100, 100, 100}, - {0, 100, 100}, - }...) - meshRes.Mesh.Triangles = append(meshRes.Mesh.Triangles, []Triangle{ - {V1: 3, V2: 2, V3: 1, PID: 5, P1: 0, P2: 0, P3: 0, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "t1"}}}}}, - {V1: 1, V2: 0, V3: 3, PID: 5, P1: 0, P2: 0, P3: 0}, - {V1: 4, V2: 5, V3: 6, PID: 5, P1: 1, P2: 1, P3: 1}, - {V1: 6, V2: 7, V3: 4, PID: 5, P1: 1, P2: 1, P3: 1}, - {V1: 0, V2: 1, V3: 5, PID: 5, P1: 0, P2: 1, P3: 2}, - {V1: 5, V2: 4, V3: 0, PID: 5, P1: 3, P2: 0, P3: 2}, - {V1: 1, V2: 2, V3: 6, PID: 5, P1: 0, P2: 1, P3: 2}, - {V1: 6, V2: 5, V3: 1, PID: 5, P1: 2, P2: 1, P3: 3}, - {V1: 2, V2: 3, V3: 7, PID: 5, P1: 0, P2: 0, P3: 0}, - {V1: 7, V2: 6, V3: 2, PID: 5, P1: 0, P2: 0, P3: 0}, - {V1: 3, V2: 0, V3: 4, PID: 5, P1: 0, P2: 0, P3: 0}, - {V1: 4, V2: 7, V3: 3, PID: 5, P1: 0, P2: 0, P3: 0}, - }...) components := &Object{ ID: 20, Type: ObjectTypeSupport, @@ -434,7 +434,7 @@ func TestDecoder_processRootModel(t *testing.T) { - + @@ -444,7 +444,7 @@ func TestDecoder_processRootModel(t *testing.T) { - + @@ -637,8 +637,8 @@ func TestDecoder_processRootModel_warns(t *testing.T) { want := []string{ fmt.Sprintf("Resources@BaseMaterials#0@Base#0: %v", specerr.NewParseAttrError("displaycolor", true)), fmt.Sprintf("Resources@BaseMaterials#1: %v", specerr.NewParseAttrError("id", true)), - fmt.Sprintf("Resources@Object#0@Mesh@Point3D#8: %v", specerr.NewParseAttrError("x", true)), - fmt.Sprintf("Resources@Object#0@Mesh@Triangle#13: %v", specerr.NewParseAttrError("v1", true)), + fmt.Sprintf("Resources@Object#0@Mesh@Vertices@Point3D#8: %v", specerr.NewParseAttrError("x", true)), + fmt.Sprintf("Resources@Object#0@Mesh@Triangles@Triangle#13: %v", specerr.NewParseAttrError("v1", true)), fmt.Sprintf("Resources@Object#1: %v", specerr.NewParseAttrError("pid", false)), fmt.Sprintf("Resources@Object#1: %v", specerr.NewParseAttrError("pindex", false)), fmt.Sprintf("Resources@Object#1: %v", specerr.NewParseAttrError("type", false)), @@ -748,14 +748,14 @@ func TestOpenReader(t *testing.T) { Language: "en-US", Path: "/3D/3dmodel.model", Resources: Resources{Objects: []*Object{ {ID: 1, Name: "Cube", Mesh: &Mesh{ - Vertices: []Point3D{ + Vertices: Vertices{Vertex: []Point3D{ {100, 100, 100}, {100, 0, 100}, {100, 100, 0}, {0, 100, 0}, {100, 0, 0}, {}, {0, 0, 100}, {0, 100, 100}, - }, Triangles: []Triangle{ + }}, Triangles: Triangles{Triangle: []Triangle{ {V1: 0, V2: 1, V3: 2}, {V1: 3, V2: 0, V3: 2}, {V1: 4, V2: 3, V3: 2}, {V1: 5, V2: 3, V3: 4}, {V1: 4, V2: 6, V3: 5}, {V1: 6, V2: 7, V3: 5}, {V1: 7, V2: 6, V3: 0}, {V1: 1, V2: 6, V3: 4}, {V1: 5, V2: 7, V3: 3}, {V1: 7, V2: 0, V3: 3}, {V1: 2, V2: 1, V3: 4}, {V1: 0, V2: 6, V3: 1}, - }}, + }}}, }, }}, Build: Build{ diff --git a/validate.go b/validate.go index dcef8d4..0cd7c22 100644 --- a/validate.go +++ b/validate.go @@ -248,16 +248,16 @@ func (r *Object) validateMesh(m *Model, path string) error { var errs error switch r.Type { case ObjectTypeModel, ObjectTypeSolidSupport: - if len(r.Mesh.Vertices) < 3 { + if len(r.Mesh.Vertices.Vertex) < 3 { errs = errors.Append(errs, errors.ErrInsufficientVertices) } - if len(r.Mesh.Triangles) <= 3 && len(r.Mesh.Any) == 0 { + if len(r.Mesh.Triangles.Triangle) <= 3 && len(r.Mesh.Any) == 0 { errs = errors.Append(errs, errors.ErrInsufficientTriangles) } } - nodeCount := uint32(len(r.Mesh.Vertices)) - for i, face := range r.Mesh.Triangles { + nodeCount := uint32(len(r.Mesh.Vertices.Vertex)) + for i, face := range r.Mesh.Triangles.Triangle { if face.V1 == face.V2 || face.V1 == face.V3 || face.V2 == face.V3 { errs = errors.Append(errs, errors.WrapIndex(errors.ErrDuplicatedIndices, face, i)) } @@ -413,16 +413,16 @@ func isSolidObject(r *Object) bool { // ValidateCoherency checks that the mesh is non-empty, manifold and oriented. func (m *Mesh) ValidateCoherency() error { - if len(m.Vertices) < 3 { + if len(m.Vertices.Vertex) < 3 { return errors.ErrInsufficientVertices } - if len(m.Triangles) <= 3 { + if len(m.Triangles.Triangle) <= 3 { return errors.ErrInsufficientTriangles } var edgeCounter uint32 pairMatching := make(pairMatch) - for _, face := range m.Triangles { + for _, face := range m.Triangles.Triangle { fv := [3]uint32{face.V1, face.V2, face.V3} for j := 0; j < 3; j++ { n1, n2 := fv[j], fv[(j+1)%3] @@ -434,7 +434,7 @@ func (m *Mesh) ValidateCoherency() error { } positive, negative := make([]uint32, edgeCounter), make([]uint32, edgeCounter) - for _, face := range m.Triangles { + for _, face := range m.Triangles.Triangle { fv := [3]uint32{face.V1, face.V2, face.V3} for j := 0; j < 3; j++ { n1, n2 := fv[j], fv[(j+1)%3] diff --git a/validate_test.go b/validate_test.go index 4572a49..848326b 100644 --- a/validate_test.go +++ b/validate_test.go @@ -49,9 +49,9 @@ func TestValidate(t *testing.T) { fmt.Sprintf("Metadata#4: %v", &errors.MissingFieldError{Name: attrName}), }}, {"build", &Model{Resources: Resources{Assets: []Asset{&BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}}, Objects: []*Object{ - {ID: 2, Type: ObjectTypeOther, Mesh: &Mesh{Vertices: []Point3D{{}, {}, {}, {}}, Triangles: []Triangle{ + {ID: 2, Type: ObjectTypeOther, Mesh: &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, Triangles: Triangles{Triangle: []Triangle{ {V1: 0, V2: 1, V3: 2}, {V1: 0, V2: 3, V3: 1}, {V1: 0, V2: 2, V3: 3}, {V1: 1, V2: 3, V3: 2}, - }}}}}, Build: Build{AnyAttr: spec.AnyAttr{&fakeAttr{}}, Items: []*Item{ + }}}}}}, Build: Build{AnyAttr: spec.AnyAttr{&fakeAttr{}}, Items: []*Item{ {}, {ObjectID: 2}, {ObjectID: 100}, @@ -89,20 +89,20 @@ func TestValidate(t *testing.T) { }, Objects: []*Object{ {}, {ID: 1, PIndex: 1, Mesh: &Mesh{}, Components: &Components{Component: []*Component{{ObjectID: 1}}}}, - {ID: 2, Mesh: &Mesh{Vertices: []Point3D{{}, {}, {}, {}}, Triangles: []Triangle{ + {ID: 2, Mesh: &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, Triangles: Triangles{Triangle: []Triangle{ {V1: 0, V2: 1, V3: 2}, {V1: 0, V2: 3, V3: 1}, {V1: 0, V2: 2, V3: 3}, {V1: 1, V2: 3, V3: 2}, - }}}, + }}}}, {ID: 3, PID: 5, Components: &Components{Component: []*Component{ {ObjectID: 3}, {ObjectID: 2}, {}, {ObjectID: 5}, {ObjectID: 100}, }}}, - {ID: 4, PID: 100, Mesh: &Mesh{Vertices: make([]Point3D, 2), Triangles: make([]Triangle, 3)}}, - {ID: 6, PID: 5, PIndex: 2, Mesh: &Mesh{Vertices: []Point3D{{}, {}, {}, {}}, - Triangles: []Triangle{ + {ID: 4, PID: 100, Mesh: &Mesh{Vertices: Vertices{Vertex: make([]Point3D, 2)}, Triangles: Triangles{Triangle: make([]Triangle, 3)}}}, + {ID: 6, PID: 5, PIndex: 2, Mesh: &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, + Triangles: Triangles{Triangle: []Triangle{ {V1: 0, V2: 1, V3: 2, PID: 5, P1: 2, P2: 0, P3: 0}, {V1: 0, V2: 1, V3: 4, PID: 5, P1: 2, P2: 2, P3: 2}, {V1: 0, V2: 2, V3: 3, PID: 5, P1: 1, P2: 1, P3: 0}, {V1: 1, V2: 2, V3: 3, PID: 100, P1: 0, P2: 0, P3: 0}, - }}}, + }}}}, }}}, []string{ fmt.Sprintf("Resources@Object#0: %v", errors.ErrMissingID), fmt.Sprintf("Resources@Object#0: %v", errors.ErrInvalidObject), @@ -160,22 +160,22 @@ func TestObject_ValidateMesh(t *testing.T) { r *Mesh wantErr bool }{ - {"few vertices", &Mesh{Vertices: make([]Point3D, 1), Triangles: make([]Triangle, 3)}, true}, - {"few triangles", &Mesh{Vertices: make([]Point3D, 3), Triangles: make([]Triangle, 3)}, true}, - {"wrong orientation", &Mesh{Vertices: []Point3D{{}, {}, {}, {}}, - Triangles: []Triangle{ + {"few vertices", &Mesh{Vertices: Vertices{Vertex: make([]Point3D, 1)}, Triangles: Triangles{Triangle: make([]Triangle, 3)}}, true}, + {"few triangles", &Mesh{Vertices: Vertices{Vertex: make([]Point3D, 3)}, Triangles: Triangles{Triangle: make([]Triangle, 3)}}, true}, + {"wrong orientation", &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, + Triangles: Triangles{Triangle: []Triangle{ {V1: 0, V2: 1, V3: 2}, {V1: 0, V2: 3, V3: 1}, {V1: 0, V2: 2, V3: 3}, {V1: 1, V2: 2, V3: 3}, - }}, true}, - {"correct", &Mesh{Vertices: []Point3D{{}, {}, {}, {}}, - Triangles: []Triangle{ + }}}, true}, + {"correct", &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, + Triangles: Triangles{Triangle: []Triangle{ {V1: 0, V2: 1, V3: 2}, {V1: 0, V2: 3, V3: 1}, {V1: 0, V2: 2, V3: 3}, {V1: 1, V2: 3, V3: 2}, - }}, false}, + }}}, false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -187,14 +187,14 @@ func TestObject_ValidateMesh(t *testing.T) { } func TestModel_ValidateCoherency(t *testing.T) { - validMesh := &Mesh{Vertices: []Point3D{{}, {}, {}, {}}, Triangles: []Triangle{ + validMesh := &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, Triangles: Triangles{Triangle: []Triangle{ {V1: 0, V2: 1, V3: 2}, {V1: 0, V2: 3, V3: 1}, {V1: 0, V2: 2, V3: 3}, {V1: 1, V2: 3, V3: 2}, - }} - invalidMesh := &Mesh{Vertices: []Point3D{{}, {}, {}, {}}, Triangles: []Triangle{ + }}} + invalidMesh := &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, Triangles: Triangles{Triangle: []Triangle{ {V1: 0, V2: 1, V3: 2}, {V1: 0, V2: 3, V3: 1}, {V1: 0, V2: 2, V3: 3}, {V1: 1, V2: 2, V3: 3}, - }} + }}} tests := []struct { name string m *Model From 54966ac57434a6a1ba834b731602cd776047797a Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 5 Oct 2021 17:01:04 +0200 Subject: [PATCH 14/23] consolidate metadatagroup --- core.go | 9 +++++++-- decoder.go | 20 ++++++++++++++++++-- encoder.go | 9 +++++---- encoder_test.go | 7 +++++-- read_test.go | 18 ++++++++++++------ validate.go | 2 +- validate_test.go | 2 +- 7 files changed, 49 insertions(+), 18 deletions(-) diff --git a/core.go b/core.go index 2b9ad28..5fe8674 100644 --- a/core.go +++ b/core.go @@ -358,12 +358,17 @@ func (r *BaseMaterials) Identify() uint32 { return r.ID } +type MetadataGroup struct { + Metadata []Metadata + AnyAttr spec.AnyAttr +} + // A Item is an in memory representation of the 3MF build item. type Item struct { ObjectID uint32 Transform Matrix PartNumber string - Metadata []Metadata + Metadata MetadataGroup AnyAttr spec.AnyAttr } @@ -396,7 +401,7 @@ type Object struct { PID uint32 PIndex uint32 Type ObjectType - Metadata []Metadata + Metadata MetadataGroup Mesh *Mesh Components *Components AnyAttr spec.AnyAttr diff --git a/decoder.go b/decoder.go index 6f7d683..390dbe2 100644 --- a/decoder.go +++ b/decoder.go @@ -104,17 +104,33 @@ func (d *modelDecoder) noCoreAttribute(a spec.XMLAttr) (err error) { type metadataGroupDecoder struct { baseDecoder - metadatas *[]Metadata + metadatas *MetadataGroup model *Model } func (d *metadataGroupDecoder) Child(name xml.Name) (child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrMetadata { - child = &metadataDecoder{metadatas: d.metadatas, model: d.model} + child = &metadataDecoder{metadatas: &d.metadatas.Metadata, model: d.model} } return } +func (d *metadataGroupDecoder) Start(attrs []spec.XMLAttr) error { + var errs error + for _, a := range attrs { + var attr spec.AttrGroup + if attr = d.metadatas.AnyAttr.Get(a.Name.Space); attr == nil { + attr = spec.NewAttrGroup(a.Name.Space, xml.Name{Space: Namespace, Local: attrMetadataGroup}) + d.metadatas.AnyAttr = append(d.metadatas.AnyAttr, attr) + } + errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) + } + if errs != nil { + return specerr.Wrap(errs, d.metadatas) + } + return errs +} + type metadataDecoder struct { baseDecoder model *Model diff --git a/encoder.go b/encoder.go index c15b6d8..b92ef42 100644 --- a/encoder.go +++ b/encoder.go @@ -271,10 +271,11 @@ func (e *Encoder) writeModel(x spec.Encoder, m *Model) error { return x.Flush() } -func (e *Encoder) writeMetadataGroup(x spec.Encoder, m []Metadata) { +func (e *Encoder) writeMetadataGroup(x spec.Encoder, m MetadataGroup) { xm := xml.StartElement{Name: xml.Name{Local: attrMetadataGroup}} + m.AnyAttr.Marshal3MF(x, &xm) x.EncodeToken(xm) - e.writeMetadata(x, m) + e.writeMetadata(x, m.Metadata) x.EncodeToken(xm.End()) } @@ -298,7 +299,7 @@ func (e *Encoder) writeBuild(x spec.Encoder, m *Model) { }) } item.AnyAttr.Marshal3MF(x, &xi) - if len(item.Metadata) != 0 { + if len(item.Metadata.Metadata) != 0 { x.SetAutoClose(false) x.EncodeToken(xi) e.writeMetadataGroup(x, item.Metadata) @@ -394,7 +395,7 @@ func (e *Encoder) writeObject(x spec.Encoder, r *Object) { r.AnyAttr.Marshal3MF(x, &xo) x.EncodeToken(xo) - if len(r.Metadata) != 0 { + if len(r.Metadata.Metadata) != 0 { e.writeMetadataGroup(x, r.Metadata) } diff --git a/encoder_test.go b/encoder_test.go index 50d6cea..e621b23 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -93,7 +93,10 @@ func TestMarshalModel(t *testing.T) { }}, { ID: 20, Type: ObjectTypeSupport, - Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}, {Name: xml.Name{Space: "qm", Local: "CustomMetadata4"}, Type: "xs:boolean", Value: "2"}}, + Metadata: MetadataGroup{Metadata: []Metadata{ + {Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}, + {Name: xml.Name{Space: "qm", Local: "CustomMetadata4"}, Type: "xs:boolean", Value: "2"}, + }}, Components: &Components{Component: []*Component{{ObjectID: 8, Transform: Matrix{3, 0, 0, 0, 0, 1, 0, 0, 0, 0, 2, 0, -66.4, -87.1, 8.8, 1}, AnyAttr: spec.AnyAttr{&fakeAttr{Value: "component_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo8"}}}}}}}, }, @@ -104,7 +107,7 @@ func TestMarshalModel(t *testing.T) { Items: []*Item{ { ObjectID: 20, PartNumber: "bob", Transform: Matrix{1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, -66.4, -87.1, 8.8, 1}, - Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}}, + Metadata: MetadataGroup{Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}}}, }, {ObjectID: 21, AnyAttr: spec.AnyAttr{&fakeAttr{Value: "item_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo5"}}}}}, }}, Metadata: []Metadata{ diff --git a/read_test.go b/read_test.go index 3ab12ef..5bf29a1 100644 --- a/read_test.go +++ b/read_test.go @@ -359,8 +359,11 @@ func TestDecoder_processRootModel(t *testing.T) { components := &Object{ ID: 20, Type: ObjectTypeSupport, - AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval6"}}}}, - Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}, {Name: xml.Name{Space: "qm", Local: "CustomMetadata4"}, Type: "xs:boolean", Value: "2"}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval6"}}}}, + Metadata: MetadataGroup{Metadata: []Metadata{ + {Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}, + {Name: xml.Name{Space: "qm", Local: "CustomMetadata4"}, Type: "xs:boolean", Value: "2"}, + }, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval12"}}}}}, Components: &Components{ AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval4"}}}}, Component: []*Component{ @@ -417,8 +420,11 @@ func TestDecoder_processRootModel(t *testing.T) { } want.Build.Items = append(want.Build.Items, &Item{ ObjectID: 20, PartNumber: "bob", Transform: Matrix{1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 3, 0, -66.4, -87.1, 8.8, 1}, - Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}}, - AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval2"}}}}, + Metadata: MetadataGroup{ + Metadata: []Metadata{{Name: xml.Name{Space: "qm", Local: "CustomMetadata3"}, Type: "xs:boolean", Value: "1"}}, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval13"}}}}, + }, + AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval2"}}}}, }) want.Metadata = append(want.Metadata, []Metadata{ {Name: xml.Name{Local: "Application"}, Value: "go3mf app"}, @@ -462,7 +468,7 @@ func TestDecoder_processRootModel(t *testing.T) { - + 1 2 @@ -478,7 +484,7 @@ func TestDecoder_processRootModel(t *testing.T) { - + 1 diff --git a/validate.go b/validate.go index 0cd7c22..954f7e6 100644 --- a/validate.go +++ b/validate.go @@ -81,7 +81,7 @@ func (item *Item) validate(m *Model) error { } else { errs = errors.Append(errs, errors.ErrMissingResource) } - return errors.Append(errs, checkMetadadata(m, item.Metadata)) + return errors.Append(errs, checkMetadadata(m, item.Metadata.Metadata)) } func (b *Build) validate(m *Model) error { diff --git a/validate_test.go b/validate_test.go index 848326b..c9dec2d 100644 --- a/validate_test.go +++ b/validate_test.go @@ -55,7 +55,7 @@ func TestValidate(t *testing.T) { {}, {ObjectID: 2}, {ObjectID: 100}, - {ObjectID: 1, Metadata: []Metadata{{Name: xml.Name{Local: "issue"}}}}, + {ObjectID: 1, Metadata: MetadataGroup{Metadata: []Metadata{{Name: xml.Name{Local: "issue"}}}}}, }}}, []string{ "Build: fake", fmt.Sprintf("Build@Item#0: %v", &errors.MissingFieldError{Name: attrObjectID}), From b6c52868d4403282c5cd77a59ae2768bb90f1c65 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 5 Oct 2021 17:03:07 +0200 Subject: [PATCH 15/23] wrap metadatagroup error --- decoder.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/decoder.go b/decoder.go index 390dbe2..07a6324 100644 --- a/decoder.go +++ b/decoder.go @@ -115,6 +115,10 @@ func (d *metadataGroupDecoder) Child(name xml.Name) (child spec.ElementDecoder) return } +func (d *metadataGroupDecoder) Wrap(err error) error { + return specerr.Wrap(err, d.metadatas) +} + func (d *metadataGroupDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { From aa20af8b23f674dfc117cbcb32e9b599be168424 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Tue, 5 Oct 2021 17:05:53 +0200 Subject: [PATCH 16/23] add modeldecoder wrapper --- decoder.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/decoder.go b/decoder.go index 07a6324..6d11c30 100644 --- a/decoder.go +++ b/decoder.go @@ -21,6 +21,10 @@ type modelDecoder struct { path string } +func (d *modelDecoder) Wrap(err error) error { + return err +} + func (d *modelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { if name.Space == Namespace { switch name.Local { From fcf929924b33cadadf7561f3d7e492fd1cbb6384 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Fri, 8 Oct 2021 14:19:29 +0200 Subject: [PATCH 17/23] move error wrapping to child --- beamlattice/decoder.go | 8 ++++---- decoder.go | 26 +++++++++++++++----------- materials/decoder.go | 8 ++++---- read.go | 8 +++----- slices/decoder.go | 8 ++++---- spec/encoding.go | 1 + spec/spec.go | 4 ---- 7 files changed, 31 insertions(+), 32 deletions(-) diff --git a/beamlattice/decoder.go b/beamlattice/decoder.go index 5e7409c..1f70e35 100644 --- a/beamlattice/decoder.go +++ b/beamlattice/decoder.go @@ -82,7 +82,7 @@ func (d *beamLatticeDecoder) Start(attrs []spec.XMLAttr) error { return nil } -func (d *beamLatticeDecoder) Wrap(err error) error { +func (d *beamLatticeDecoder) WrapError(err error) error { return specerr.Wrap(err, d.beamLattice) } @@ -115,7 +115,7 @@ func (d *beamsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *beamsDecoder) Wrap(err error) error { +func (d *beamsDecoder) WrapError(err error) error { return specerr.Wrap(err, &d.beamLattice.Beams) } @@ -204,7 +204,7 @@ func (d *beamSetsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *beamSetsDecoder) Wrap(err error) error { +func (d *beamSetsDecoder) WrapError(err error) error { return specerr.Wrap(err, &d.beamLattice.BeamSets) } @@ -235,7 +235,7 @@ func (d *beamSetDecoder) Start(attrs []spec.XMLAttr) error { return nil } -func (d *beamSetDecoder) Wrap(err error) error { +func (d *beamSetDecoder) WrapError(err error) error { return specerr.WrapIndex(err, &d.beamSet, len(d.beamLattice.BeamSets.BeamSet)) } diff --git a/decoder.go b/decoder.go index 6d11c30..c74eb5e 100644 --- a/decoder.go +++ b/decoder.go @@ -21,7 +21,7 @@ type modelDecoder struct { path string } -func (d *modelDecoder) Wrap(err error) error { +func (d *modelDecoder) WrapError(err error) error { return err } @@ -119,7 +119,7 @@ func (d *metadataGroupDecoder) Child(name xml.Name) (child spec.ElementDecoder) return } -func (d *metadataGroupDecoder) Wrap(err error) error { +func (d *metadataGroupDecoder) WrapError(err error) error { return specerr.Wrap(err, d.metadatas) } @@ -202,7 +202,7 @@ func (d *buildDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *buildDecoder) Wrap(err error) error { +func (d *buildDecoder) WrapError(err error) error { return specerr.Wrap(err, d.build) } @@ -233,7 +233,7 @@ func (d *buildItemDecoder) End() { d.build.Items = append(d.build.Items, &d.item) } -func (d *buildItemDecoder) Wrap(err error) error { +func (d *buildItemDecoder) WrapError(err error) error { return specerr.WrapIndex(err, &d.item, len(d.build.Items)) } @@ -306,7 +306,7 @@ func (d *resourceDecoder) Start(attrs []spec.XMLAttr) error { return errs } -func (d *resourceDecoder) Wrap(err error) error { +func (d *resourceDecoder) WrapError(err error) error { return specerr.Wrap(err, d.resources) } @@ -337,7 +337,7 @@ func (d *baseMaterialsDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *baseMaterialsDecoder) Wrap(err error) error { +func (d *baseMaterialsDecoder) WrapError(err error) error { return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) } @@ -435,7 +435,7 @@ func (d *meshDecoder) Start(attrs []spec.XMLAttr) error { return errs } -func (d *meshDecoder) Wrap(err error) error { +func (d *meshDecoder) WrapError(err error) error { return specerr.Wrap(err, d.resource.Mesh) } @@ -484,7 +484,7 @@ func (d *verticesDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *verticesDecoder) Wrap(err error) error { +func (d *verticesDecoder) WrapError(err error) error { return specerr.Wrap(err, &d.mesh.Vertices) } @@ -558,7 +558,7 @@ func (d *trianglesDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *trianglesDecoder) Wrap(err error) error { +func (d *trianglesDecoder) WrapError(err error) error { return specerr.Wrap(err, &d.resource.Mesh.Triangles) } @@ -668,7 +668,7 @@ func (d *objectDecoder) Start(attrs []spec.XMLAttr) error { return errs } -func (d *objectDecoder) Wrap(err error) error { +func (d *objectDecoder) WrapError(err error) error { return specerr.WrapIndex(err, &d.resource, len(d.resources.Objects)) } @@ -754,7 +754,7 @@ func (d *componentsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { return } -func (d *componentsDecoder) Wrap(err error) error { +func (d *componentsDecoder) WrapError(err error) error { return specerr.Wrap(err, d.resource.Components) } @@ -812,6 +812,10 @@ type topLevelDecoder struct { path string } +func (d *topLevelDecoder) WrapError(err error) error { + return err +} + func (d *topLevelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { modelName := xml.Name{Space: Namespace, Local: attrModel} if name == modelName { diff --git a/materials/decoder.go b/materials/decoder.go index 59aa78a..f3485ce 100644 --- a/materials/decoder.go +++ b/materials/decoder.go @@ -44,7 +44,7 @@ func (d *colorGroupDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *colorGroupDecoder) Wrap(err error) error { +func (d *colorGroupDecoder) WrapError(err error) error { return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) } @@ -135,7 +135,7 @@ func (d *tex2DGroupDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *tex2DGroupDecoder) Wrap(err error) error { +func (d *tex2DGroupDecoder) WrapError(err error) error { return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) } @@ -226,7 +226,7 @@ func (d *compositeMaterialsDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *compositeMaterialsDecoder) Wrap(err error) error { +func (d *compositeMaterialsDecoder) WrapError(err error) error { return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) } @@ -312,7 +312,7 @@ func (d *multiPropertiesDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *multiPropertiesDecoder) Wrap(err error) error { +func (d *multiPropertiesDecoder) WrapError(err error) error { return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) } diff --git a/read.go b/read.go index e0b3245..d297dbc 100644 --- a/read.go +++ b/read.go @@ -63,7 +63,7 @@ func (r *ReadCloser) Close() error { func decodeModelFile(ctx context.Context, r io.Reader, model *Model, path string, isRoot, strict bool) error { x := xml3mf.NewDecoder(r) - state, names := make([]spec.ElementDecoder, 0, 10), make([]xml.Name, 0, 10) + state, names := make([]spec.ChildElementDecoder, 0, 10), make([]xml.Name, 0, 10) var ( currentDecoder, tmpDecoder spec.ElementDecoder @@ -76,16 +76,14 @@ func decodeModelFile(ctx context.Context, r io.Reader, model *Model, path string if childDecoder, ok := currentDecoder.(spec.ChildElementDecoder); ok { tmpDecoder = childDecoder.Child(tp.Name) if tmpDecoder != nil { - state = append(state, currentDecoder) + state = append(state, childDecoder) names = append(names, currentName) currentName = tp.Name currentDecoder = tmpDecoder err := currentDecoder.Start(*(*[]spec.XMLAttr)(unsafe.Pointer(&tp.Attr))) if err != nil { for i := len(state) - 1; i >= 0; i-- { - if ew, ok := state[i].(spec.ErrorWrapper); ok { - err = ew.Wrap(err) - } + err = state[i].WrapError(err) } specerr.Append(&errs, err) } diff --git a/slices/decoder.go b/slices/decoder.go index 931aeaa..dc76a44 100644 --- a/slices/decoder.go +++ b/slices/decoder.go @@ -57,7 +57,7 @@ func (d *sliceStackDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *sliceStackDecoder) Wrap(err error) error { +func (d *sliceStackDecoder) WrapError(err error) error { return specerr.WrapIndex(err, d.resource, len(d.resources.Assets)) } @@ -139,7 +139,7 @@ func (d *sliceDecoder) End() { d.resource.Slices = append(d.resource.Slices, d.slice) } -func (d *sliceDecoder) Wrap(err error) error { +func (d *sliceDecoder) WrapError(err error) error { return specerr.WrapIndex(err, &d.slice, len(d.resource.Slices)) } @@ -192,7 +192,7 @@ func (d *polygonVerticesDecoder) Child(name xml.Name) (child spec.ElementDecoder return } -func (d *polygonVerticesDecoder) Wrap(err error) error { +func (d *polygonVerticesDecoder) WrapError(err error) error { return specerr.Wrap(err, &d.slice.Vertices) } @@ -231,7 +231,7 @@ type polygonDecoder struct { polygonSegmentDecoder polygonSegmentDecoder } -func (d *polygonDecoder) Wrap(err error) error { +func (d *polygonDecoder) WrapError(err error) error { index := len(d.slice.Polygons) - 1 return specerr.WrapIndex(err, &d.slice.Polygons[index], index) } diff --git a/spec/encoding.go b/spec/encoding.go index e31d6ee..5902a08 100644 --- a/spec/encoding.go +++ b/spec/encoding.go @@ -28,6 +28,7 @@ type ElementDecoder interface { type ChildElementDecoder interface { ElementDecoder Child(xml.Name) ElementDecoder + WrapError(error) error } // CharDataElementDecoder must be implemented by element decoders diff --git a/spec/spec.go b/spec/spec.go index 5f3c0eb..a5c256d 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -70,10 +70,6 @@ type Relationship struct { ID string } -type ErrorWrapper interface { - Wrap(error) error -} - // AttrGroup defines a container for different attributes of the same namespace. // It supports encoding and decoding to XML. type AttrGroup interface { From c35c0cd9a8682eca06183a36ef3672322d5f96b6 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 13 Oct 2021 09:48:26 +0200 Subject: [PATCH 18/23] improve spec error wrapping --- beamlattice/decoder.go | 44 +++-------- beamlattice/validate.go | 10 +-- core.go | 21 ++++++ decoder.go | 159 ++++++++++++---------------------------- errors/errors.go | 34 ++++----- materials/decoder.go | 65 ++++------------ materials/materials.go | 26 +++++++ materials/validate.go | 8 +- production/validate.go | 10 +-- read.go | 31 +++++--- read_test.go | 27 ++++--- slices/decoder.go | 61 ++++----------- slices/slices.go | 6 ++ slices/validate.go | 28 +++---- spec.go | 23 ------ spec/encoding.go | 3 +- spec/spec.go | 1 - spec/unknown.go | 9 +++ validate.go | 70 +++++++++--------- validate_test.go | 102 +++++++++++++------------- 20 files changed, 316 insertions(+), 422 deletions(-) delete mode 100644 spec.go diff --git a/beamlattice/decoder.go b/beamlattice/decoder.go index 1f70e35..7ae8c8c 100644 --- a/beamlattice/decoder.go +++ b/beamlattice/decoder.go @@ -76,22 +76,17 @@ func (d *beamLatticeDecoder) Start(attrs []spec.XMLAttr) error { } } } - if errs != nil { - return specerr.Wrap(errs, d.beamLattice) - } - return nil -} - -func (d *beamLatticeDecoder) WrapError(err error) error { - return specerr.Wrap(err, d.beamLattice) + return errs } -func (d *beamLatticeDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *beamLatticeDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace { if name.Local == attrBeams { child = &beamsDecoder{beamLattice: d.beamLattice} + i = -1 } else if name.Local == attrBeamSets { child = &beamSetsDecoder{beamLattice: d.beamLattice} + i = -1 } } return @@ -108,17 +103,14 @@ func (d *beamsDecoder) Start(_ []spec.XMLAttr) error { return nil } -func (d *beamsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *beamsDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrBeam { child = &d.beamDecoder + i = len(d.beamDecoder.beamLattice.Beams.Beam) } return } -func (d *beamsDecoder) WrapError(err error) error { - return specerr.Wrap(err, &d.beamLattice.Beams) -} - type beamDecoder struct { baseDecoder beamLattice *BeamLattice @@ -186,10 +178,7 @@ func (d *beamDecoder) Start(attrs []spec.XMLAttr) error { beam.CapMode[1] = d.beamLattice.CapMode } d.beamLattice.Beams.Beam = append(d.beamLattice.Beams.Beam, beam) - if errs != nil { - return specerr.WrapIndex(errs, beam, len(d.beamLattice.Beams.Beam)-1) - } - return nil + return errs } type beamSetsDecoder struct { @@ -197,17 +186,14 @@ type beamSetsDecoder struct { beamLattice *BeamLattice } -func (d *beamSetsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *beamSetsDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrBeamSet { child = &beamSetDecoder{beamLattice: d.beamLattice} + i = len(d.beamLattice.BeamSets.BeamSet) } return } -func (d *beamSetsDecoder) WrapError(err error) error { - return specerr.Wrap(err, &d.beamLattice.BeamSets) -} - type beamSetDecoder struct { baseDecoder beamLattice *BeamLattice @@ -235,13 +221,10 @@ func (d *beamSetDecoder) Start(attrs []spec.XMLAttr) error { return nil } -func (d *beamSetDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, &d.beamSet, len(d.beamLattice.BeamSets.BeamSet)) -} - -func (d *beamSetDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *beamSetDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrRef { child = &d.beamRefDecoder + i = -1 } return } @@ -267,10 +250,7 @@ func (d *beamRefDecoder) Start(attrs []spec.XMLAttr) error { } } d.beamSet.Refs = append(d.beamSet.Refs, uint32(val)) - if errs != nil { - return specerr.WrapIndex(errs, uint32(0), len(d.beamSet.Refs)-1) - } - return nil + return errs } type baseDecoder struct { diff --git a/beamlattice/validate.go b/beamlattice/validate.go index a08d0bd..887dc16 100644 --- a/beamlattice/validate.go +++ b/beamlattice/validate.go @@ -48,27 +48,27 @@ func validateObject(m *go3mf.Model, path string, obj *go3mf.Object) error { for i, b := range bl.Beams.Beam { if b.Indices[0] == b.Indices[1] { - errs = errors.Append(errs, errors.WrapIndex(ErrLatticeSameVertex, b, i)) + errs = errors.Append(errs, errors.WrapIndex(ErrLatticeSameVertex, attrBeam, i)) } else { l := len(obj.Mesh.Vertices.Vertex) if int(b.Indices[0]) >= l || int(b.Indices[1]) >= l { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, b, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, attrBeam, i)) } } if b.Radius[0] != 0 && b.Radius[0] != bl.Radius && b.Radius[0] != b.Radius[1] { - errs = errors.Append(errs, errors.WrapIndex(ErrLatticeBeamR2, b, i)) + errs = errors.Append(errs, errors.WrapIndex(ErrLatticeBeamR2, attrBeam, i)) } } for i, set := range bl.BeamSets.BeamSet { for _, ref := range set.Refs { if int(ref) >= len(set.Refs) { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, set, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, attrBeamSet, i)) break } } } if errs != nil { - errs = errors.Wrap(errors.Wrap(errs, bl), obj.Mesh) + errs = errors.Wrap(errors.Wrap(errs, attrBeamLattice), "mesh") } return errs } diff --git a/core.go b/core.go index 5fe8674..3d635a3 100644 --- a/core.go +++ b/core.go @@ -91,6 +91,7 @@ func (o ObjectType) String() string { // Asset defines build resource. type Asset interface { + XMLName() xml.Name Identify() uint32 } @@ -358,6 +359,11 @@ func (r *BaseMaterials) Identify() uint32 { return r.ID } +// XMLName returns the xml identifier of the resource. +func (BaseMaterials) XMLName() xml.Name { + return xml.Name{Space: Namespace, Local: attrBaseMaterials} +} + type MetadataGroup struct { Metadata []Metadata AnyAttr spec.AnyAttr @@ -538,6 +544,17 @@ func (mb *MeshBuilder) AddVertex(node Point3D) uint32 { return index } +// UnknownAsset wraps a spec.UnknownTokens to fulfill +// the Asset interface. +type UnknownAsset struct { + spec.UnknownTokens + id uint32 +} + +func (u UnknownAsset) Identify() uint32 { + return u.id +} + func newObjectType(s string) (o ObjectType, ok bool) { o, ok = map[string]ObjectType{ "model": ObjectTypeModel, @@ -561,6 +578,10 @@ func newUnits(s string) (u Units, ok bool) { return } +type objectPather interface { + ObjectPath() string +} + const ( nsXML = "http://www.w3.org/XML/1998/namespace" nsXMLNs = "http://www.w3.org/2000/xmlns/" diff --git a/decoder.go b/decoder.go index c74eb5e..139fbb8 100644 --- a/decoder.go +++ b/decoder.go @@ -21,29 +21,30 @@ type modelDecoder struct { path string } -func (d *modelDecoder) WrapError(err error) error { - return err -} - -func (d *modelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *modelDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace { switch name.Local { case attrResources: resources, _ := d.model.FindResources(d.path) child = &resourceDecoder{resources: resources, model: d.model} + i = -1 case attrBuild: if d.isRoot { child = &buildDecoder{build: &d.model.Build, model: d.model} + i = -1 } case attrMetadata: if d.isRoot { child = &metadataDecoder{metadatas: &d.model.Metadata, model: d.model} + i = len(d.model.Metadata) } } } else if ext, ok := spec.Load(name.Space); ok { child = ext.NewElementDecoder(d.model, name.Local) + i = -1 } else { child = spec.NewAnyUnknownDecoder(name, &d.model.Any) + i = -1 } return } @@ -112,17 +113,14 @@ type metadataGroupDecoder struct { model *Model } -func (d *metadataGroupDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *metadataGroupDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrMetadata { child = &metadataDecoder{metadatas: &d.metadatas.Metadata, model: d.model} + i = len(d.metadatas.Metadata) } return } -func (d *metadataGroupDecoder) WrapError(err error) error { - return specerr.Wrap(err, d.metadatas) -} - func (d *metadataGroupDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { @@ -133,9 +131,6 @@ func (d *metadataGroupDecoder) Start(attrs []spec.XMLAttr) error { } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } - if errs != nil { - return specerr.Wrap(errs, d.metadatas) - } return errs } @@ -195,17 +190,14 @@ type buildDecoder struct { build *Build } -func (d *buildDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *buildDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrItem { child = &buildItemDecoder{build: d.build, model: d.model} + i = len(d.build.Items) } return } -func (d *buildDecoder) WrapError(err error) error { - return specerr.Wrap(err, d.build) -} - func (d *buildDecoder) Start(attrs []spec.XMLAttr) error { var errs error for _, a := range attrs { @@ -216,9 +208,6 @@ func (d *buildDecoder) Start(attrs []spec.XMLAttr) error { } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } - if errs != nil { - return specerr.Wrap(errs, d.build) - } return errs } @@ -233,13 +222,10 @@ func (d *buildItemDecoder) End() { d.build.Items = append(d.build.Items, &d.item) } -func (d *buildItemDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, &d.item, len(d.build.Items)) -} - -func (d *buildItemDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *buildItemDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrMetadataGroup { child = &metadataGroupDecoder{metadatas: &d.item.Metadata, model: d.model} + i = -1 } return } @@ -258,9 +244,6 @@ func (d *buildItemDecoder) Start(attrs []spec.XMLAttr) error { errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } } - if errs != nil { - return specerr.WrapIndex(errs, &d.item, len(d.build.Items)) - } return errs } @@ -300,28 +283,25 @@ func (d *resourceDecoder) Start(attrs []spec.XMLAttr) error { } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } - if errs != nil { - return specerr.Wrap(errs, d.resources) - } return errs } -func (d *resourceDecoder) WrapError(err error) error { - return specerr.Wrap(err, d.resources) -} - -func (d *resourceDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *resourceDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace { switch name.Local { case attrObject: child = &objectDecoder{resources: d.resources, model: d.model} + i = len(d.resources.Objects) case attrBaseMaterials: child = &baseMaterialsDecoder{resources: d.resources} + i = len(d.resources.Assets) } } else if ext, ok := spec.Load(name.Space); ok { child = ext.NewElementDecoder(d.resources, name.Local) + i = len(d.resources.Assets) } else { child = &unknownAssetDecoder{UnknownTokensDecoder: *spec.NewUnknownDecoder(name), resources: d.resources} + i = len(d.resources.Assets) } return } @@ -337,13 +317,10 @@ func (d *baseMaterialsDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *baseMaterialsDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) -} - -func (d *baseMaterialsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *baseMaterialsDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrBase { child = &d.baseMaterialDecoder + i = len(d.resource.Materials) } return } @@ -369,10 +346,7 @@ func (d *baseMaterialsDecoder) Start(attrs []spec.XMLAttr) error { errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } } - if errs != nil { - return specerr.WrapIndex(errs, &d.resource, len(d.resources.Assets)) - } - return nil + return errs } type baseMaterialDecoder struct { @@ -407,10 +381,7 @@ func (d *baseMaterialDecoder) Start(attrs []spec.XMLAttr) error { } } d.resource.Materials = append(d.resource.Materials, base) - if errs != nil { - return specerr.WrapIndex(errs, base, len(d.resource.Materials)-1) - } - return nil + return errs } type meshDecoder struct { @@ -429,27 +400,24 @@ func (d *meshDecoder) Start(attrs []spec.XMLAttr) error { } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } - if errs != nil { - return specerr.Wrap(errs, d.resource.Mesh) - } return errs } -func (d *meshDecoder) WrapError(err error) error { - return specerr.Wrap(err, d.resource.Mesh) -} - -func (d *meshDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *meshDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace { if name.Local == attrVertices { child = &verticesDecoder{mesh: d.resource.Mesh} + i = -1 } else if name.Local == attrTriangles { child = &trianglesDecoder{resource: d.resource} + i = -1 } } else if ext, ok := spec.Load(name.Space); ok { child = ext.NewElementDecoder(d.resource.Mesh, name.Local) + i = -1 } else { child = spec.NewAnyUnknownDecoder(name, &d.resource.Mesh.Any) + i = -1 } return } @@ -471,23 +439,17 @@ func (d *verticesDecoder) Start(attrs []spec.XMLAttr) error { } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } - if errs != nil { - return specerr.Wrap(errs, &d.mesh.Vertices) - } - return nil + return errs } -func (d *verticesDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *verticesDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrVertex { child = &d.vertexDecoder + i = len(d.mesh.Vertices.Vertex) } return } -func (d *verticesDecoder) WrapError(err error) error { - return specerr.Wrap(err, &d.mesh.Vertices) -} - type vertexDecoder struct { baseDecoder mesh *Mesh @@ -516,10 +478,7 @@ func (d *vertexDecoder) Start(attrs []spec.XMLAttr) error { } } d.mesh.Vertices.Vertex = append(d.mesh.Vertices.Vertex, Point3D{x, y, z}) - if errs != nil { - return specerr.WrapIndex(errs, Point3D{x, y, z}, len(d.mesh.Vertices.Vertex)-1) - } - return nil + return errs } type trianglesDecoder struct { @@ -545,23 +504,17 @@ func (d *trianglesDecoder) Start(attrs []spec.XMLAttr) error { } errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } - if errs != nil { - return specerr.Wrap(errs, &d.resource.Mesh.Triangles) - } - return nil + return errs } -func (d *trianglesDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *trianglesDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrTriangle { child = &d.triangleDecoder + i = len(d.resource.Mesh.Triangles.Triangle) } return } -func (d *trianglesDecoder) WrapError(err error) error { - return specerr.Wrap(err, &d.resource.Mesh.Triangles) -} - type triangleDecoder struct { baseDecoder mesh *Mesh @@ -624,10 +577,7 @@ func (d *triangleDecoder) Start(attrs []spec.XMLAttr) error { t.PID = pid t.P1, t.P2, t.P3 = p1, p2, p3 d.mesh.Triangles.Triangle = append(d.mesh.Triangles.Triangle, t) - if errs != nil { - return specerr.WrapIndex(errs, t, len(d.mesh.Triangles.Triangle)-1) - } - return nil + return errs } func applyDefault(val, defVal uint32, noDef bool) uint32 { @@ -662,24 +612,20 @@ func (d *objectDecoder) Start(attrs []spec.XMLAttr) error { errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } } - if errs != nil { - return specerr.WrapIndex(errs, &d.resource, len(d.resources.Objects)) - } return errs } -func (d *objectDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, &d.resource, len(d.resources.Objects)) -} - -func (d *objectDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *objectDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace { if name.Local == attrMesh { child = &meshDecoder{resource: &d.resource} + i = -1 } else if name.Local == attrComponents { child = &componentsDecoder{resource: &d.resource} + i = -1 } else if name.Local == attrMetadataGroup { child = &metadataGroupDecoder{metadatas: &d.resource.Metadata, model: d.model} + i = -1 } } return @@ -741,23 +687,17 @@ func (d *componentsDecoder) Start(attrs []spec.XMLAttr) error { errs = specerr.Append(errs, attr.Unmarshal3MFAttr(a)) } d.resource.Components = components - if errs != nil { - return specerr.Wrap(errs, d.resource.Components) - } - return nil + return errs } -func (d *componentsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *componentsDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrComponent { child = &d.componentDecoder + i = len(d.resource.Components.Component) } return } -func (d *componentsDecoder) WrapError(err error) error { - return specerr.Wrap(err, d.resource.Components) -} - type componentDecoder struct { baseDecoder resource *Object @@ -793,10 +733,7 @@ func (d *componentDecoder) Start(attrs []spec.XMLAttr) error { } } d.resource.Components.Component = append(d.resource.Components.Component, &component) - if errs != nil { - return specerr.WrapIndex(errs, &component, len(d.resource.Components.Component)-1) - } - return nil + return errs } type baseDecoder struct { @@ -812,14 +749,11 @@ type topLevelDecoder struct { path string } -func (d *topLevelDecoder) WrapError(err error) error { - return err -} - -func (d *topLevelDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *topLevelDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { modelName := xml.Name{Space: Namespace, Local: attrModel} if name == modelName { child = &modelDecoder{model: d.model, isRoot: d.isRoot, path: d.path} + i = -1 } return } @@ -842,10 +776,7 @@ func (d *unknownAssetDecoder) Start(attrs []spec.XMLAttr) (errs error) { break } } - if errs != nil { - return specerr.WrapIndex(errs, &d.resource, len(d.resources.Assets)) - } - return nil + return errs } func (d *unknownAssetDecoder) End() { diff --git a/errors/errors.go b/errors/errors.go index c20ece0..0cbb5a0 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -39,21 +39,15 @@ var ( ) type Level struct { - Element interface{} - Index int // -1 if not needed + Name string + Index int // -1 if not needed } func (l *Level) String() string { - name := fmt.Sprintf("%T", l.Element) - s := strings.Split(name, ".") - if len(s) > 0 { - name = s[len(s)-1] // remove package name - } - name = strings.Replace(name, "*", "", -1) if l.Index == -1 { - return name + return l.Name } - return fmt.Sprintf("%s#%d", name, l.Index) + return fmt.Sprintf("%s#%d", l.Name, l.Index) } type Error struct { @@ -62,43 +56,43 @@ type Error struct { Path string } -func Wrap(err error, element interface{}) error { - return WrapIndex(err, element, -1) +func Wrap(err error, name string) error { + return WrapIndex(err, name, -1) } -func WrapIndex(err error, element interface{}, index int) error { +func WrapIndex(err error, name string, index int) error { if err == nil { return nil } if e, ok := err.(*Error); ok { - e.Target = append(e.Target, Level{element, index}) + e.Target = append(e.Target, Level{name, index}) return e } if e, ok := err.(*List); ok { for i, e1 := range e.Errors { - e.Errors[i] = WrapIndex(e1, element, index) + e.Errors[i] = WrapIndex(e1, name, index) } return e } - return &Error{Target: []Level{{element, index}}, Err: err} + return &Error{Target: []Level{{name, index}}, Err: err} } -func WrapPath(err error, element interface{}, path string) error { +func WrapPath(err error, name string, path string) error { if err == nil { return nil } if e, ok := err.(*Error); ok { e.Path = path - e.Target = append(e.Target, Level{element, -1}) + e.Target = append(e.Target, Level{name, -1}) return e } if e, ok := err.(*List); ok { for i, e1 := range e.Errors { - e.Errors[i] = WrapPath(e1, element, path) + e.Errors[i] = WrapPath(e1, name, path) } return e } - return &Error{Target: []Level{{element, -1}}, Err: err, Path: path} + return &Error{Target: []Level{{name, -1}}, Err: err, Path: path} } func (e *Error) Unwrap() error { diff --git a/materials/decoder.go b/materials/decoder.go index f3485ce..3358bac 100644 --- a/materials/decoder.go +++ b/materials/decoder.go @@ -44,13 +44,10 @@ func (d *colorGroupDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *colorGroupDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) -} - -func (d *colorGroupDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *colorGroupDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrColor { child = &d.colorDecoder + i = len(d.resource.Colors) } return } @@ -84,7 +81,7 @@ func (d *colorDecoder) Start(attrs []spec.XMLAttr) error { } d.resource.Colors = append(d.resource.Colors, c) if err != nil { - return specerr.WrapIndex(err, c, len(d.resource.Colors)-1) + return err } break } @@ -118,10 +115,7 @@ func (d *tex2DCoordDecoder) Start(attrs []spec.XMLAttr) error { } } d.resource.Coords = append(d.resource.Coords, text) - if errs != nil { - return specerr.WrapIndex(errs, text, len(d.resource.Coords)-1) - } - return nil + return errs } type tex2DGroupDecoder struct { @@ -135,13 +129,10 @@ func (d *tex2DGroupDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *tex2DGroupDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) -} - -func (d *tex2DGroupDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *tex2DGroupDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrTex2DCoord { child = &d.tex2DCoordDecoder + i = len(d.resource.Coords) } return } @@ -168,10 +159,7 @@ func (d *tex2DGroupDecoder) Start(attrs []spec.XMLAttr) error { d.resource.TextureID = uint32(val) } } - if errs != nil { - return specerr.WrapIndex(errs, &d.resource, len(d.resources.Assets)) - } - return nil + return errs } type texture2DDecoder struct { @@ -209,10 +197,7 @@ func (d *texture2DDecoder) Start(attrs []spec.XMLAttr) error { d.resource.Filter, _ = newTextureFilter(string(a.Value)) } } - if errs != nil { - return specerr.WrapIndex(errs, &d.resource, len(d.resources.Assets)) - } - return nil + return errs } type compositeMaterialsDecoder struct { @@ -226,13 +211,10 @@ func (d *compositeMaterialsDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *compositeMaterialsDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) -} - -func (d *compositeMaterialsDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *compositeMaterialsDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrComposite { child = &d.compositeDecoder + i = len(d.resource.Composites) } return } @@ -267,10 +249,7 @@ func (d *compositeMaterialsDecoder) Start(attrs []spec.XMLAttr) error { } } } - if errs != nil { - return specerr.WrapIndex(errs, &d.resource, len(d.resources.Assets)) - } - return nil + return errs } type compositeDecoder struct { @@ -295,10 +274,7 @@ func (d *compositeDecoder) Start(attrs []spec.XMLAttr) error { } } d.resource.Composites = append(d.resource.Composites, composite) - if errs != nil { - return specerr.WrapIndex(errs, composite, len(d.resource.Composites)-1) - } - return nil + return errs } type multiPropertiesDecoder struct { @@ -312,13 +288,10 @@ func (d *multiPropertiesDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *multiPropertiesDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, &d.resource, len(d.resources.Assets)) -} - -func (d *multiPropertiesDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *multiPropertiesDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrMulti { child = &d.multiDecoder + i = len(d.resource.Multis) } return } @@ -352,10 +325,7 @@ func (d *multiPropertiesDecoder) Start(attrs []spec.XMLAttr) error { } } } - if errs != nil { - return specerr.WrapIndex(errs, &d.resource, len(d.resources.Assets)) - } - return nil + return errs } type multiDecoder struct { @@ -380,10 +350,7 @@ func (d *multiDecoder) Start(attrs []spec.XMLAttr) error { } } d.resource.Multis = append(d.resource.Multis, multi) - if errs != nil { - return specerr.WrapIndex(errs, &d.resource, len(d.resource.Multis)-1) - } - return nil + return errs } type baseDecoder struct { diff --git a/materials/materials.go b/materials/materials.go index e0ae3d3..c7cbe58 100644 --- a/materials/materials.go +++ b/materials/materials.go @@ -4,6 +4,7 @@ package materials import ( + "encoding/xml" "errors" "image/color" @@ -125,6 +126,11 @@ func (t *Texture2D) Identify() uint32 { return t.ID } +// XMLName returns the xml identifier of the resource. +func (Texture2D) XMLName() xml.Name { + return xml.Name{Space: Namespace, Local: attrTexture2D} +} + // TextureCoord map a vertex of a triangle to a position in image space (U, V coordinates) type TextureCoord [2]float32 @@ -155,6 +161,11 @@ func (r *Texture2DGroup) Identify() uint32 { return r.ID } +// XMLName returns the xml identifier of the resource. +func (Texture2DGroup) XMLName() xml.Name { + return xml.Name{Space: Namespace, Local: attrTexture2DGroup} +} + // ColorGroup acts as a container for color properties. type ColorGroup struct { ID uint32 @@ -171,6 +182,11 @@ func (c *ColorGroup) Identify() uint32 { return c.ID } +// XMLName returns the xml identifier of the resource. +func (ColorGroup) XMLName() xml.Name { + return xml.Name{Space: Namespace, Local: attrColorGroup} +} + // A Composite specifies the proportion of the overall mixture for each material. type Composite struct { Values []float32 @@ -194,6 +210,11 @@ func (c *CompositeMaterials) Identify() uint32 { return c.ID } +// XMLName returns the xml identifier of the resource. +func (CompositeMaterials) XMLName() xml.Name { + return xml.Name{Space: Namespace, Local: attrCompositematerials} +} + // The Multi element combines the constituent materials and properties. type Multi struct { PIndices []uint32 @@ -218,6 +239,11 @@ func (c *MultiProperties) Identify() uint32 { return c.ID } +// XMLName returns the xml identifier of the resource. +func (MultiProperties) XMLName() xml.Name { + return xml.Name{Space: Namespace, Local: attrMultiProps} +} + func newTexture2DType(s string) (t Texture2DType, ok bool) { t, ok = map[string]Texture2DType{ "image/png": TextureTypePNG, diff --git a/materials/validate.go b/materials/validate.go index d89dc7f..a3e8eed 100644 --- a/materials/validate.go +++ b/materials/validate.go @@ -43,7 +43,7 @@ func validateColorGroup(path string, r *ColorGroup) (errs error) { } for j, c := range r.Colors { if c == (color.RGBA{}) { - errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrColor), c, j)) + errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrColor), attrColor, j)) } } return @@ -137,10 +137,10 @@ func validateMultiProps(m *go3mf.Model, path string, r *MultiProperties) (errs e errs = errors.Append(errs, errors.ErrMissingResource) } } - for j, m := range r.Multis { - for k, index := range m.PIndices { + for j, multi := range r.Multis { + for k, index := range multi.PIndices { if k < len(r.PIDs) && lengths[k] < int(index) { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, m, j)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, attrMulti, j)) break } } diff --git a/production/validate.go b/production/validate.go index 735b34d..29a01cf 100644 --- a/production/validate.go +++ b/production/validate.go @@ -28,9 +28,9 @@ func validateModel(m *go3mf.Model) error { var errs error u := GetBuildAttr(&m.Build) if u == nil { - errs = errors.Append(errs, errors.Wrap(errors.NewMissingFieldError(attrProdUUID), m.Build)) + errs = errors.Append(errs, errors.Wrap(errors.NewMissingFieldError(attrProdUUID), "build")) } else if uuid.Validate(u.UUID) != nil { - errs = errors.Append(errs, errors.Wrap(ErrUUID, m.Build)) + errs = errors.Append(errs, errors.Wrap(ErrUUID, "build")) } for i, item := range m.Build.Items { var iErrs error @@ -41,7 +41,7 @@ func validateModel(m *go3mf.Model) error { iErrs = errors.Append(iErrs, errors.NewMissingFieldError(attrProdUUID)) } if iErrs != nil { - errs = errors.Append(errs, errors.Wrap(errors.WrapIndex(iErrs, item, i), m.Build)) + errs = errors.Append(errs, errors.Wrap(errors.WrapIndex(iErrs, "item", i), "build")) } } return errs @@ -65,11 +65,11 @@ func validateObject(m *go3mf.Model, path string, obj *go3mf.Object) error { err = errors.Append(err, errors.NewMissingFieldError(attrProdUUID)) } if err != nil { - cErrs = errors.Append(cErrs, errors.WrapIndex(err, c, i)) + cErrs = errors.Append(cErrs, errors.WrapIndex(err, "component", i)) } } if cErrs != nil { - errs = errors.Append(errs, errors.Wrap(cErrs, obj.Components)) + errs = errors.Append(errs, errors.Wrap(cErrs, "components")) } } return errs diff --git a/read.go b/read.go index d297dbc..9a474e0 100644 --- a/read.go +++ b/read.go @@ -63,27 +63,32 @@ func (r *ReadCloser) Close() error { func decodeModelFile(ctx context.Context, r io.Reader, model *Model, path string, isRoot, strict bool) error { x := xml3mf.NewDecoder(r) - state, names := make([]spec.ChildElementDecoder, 0, 10), make([]xml.Name, 0, 10) + type stackElement struct { + decoder spec.ElementDecoder + name xml.Name + i int + } + stack := make([]stackElement, 0, 10) var ( - currentDecoder, tmpDecoder spec.ElementDecoder - currentName xml.Name - errs specerr.List + currentDecoder spec.ElementDecoder + currentName xml.Name + errs specerr.List ) currentDecoder = &topLevelDecoder{isRoot: isRoot, model: model, path: path} var err error x.OnStart = func(tp xml3mf.StartElement) { if childDecoder, ok := currentDecoder.(spec.ChildElementDecoder); ok { - tmpDecoder = childDecoder.Child(tp.Name) + i, tmpDecoder := childDecoder.Child(tp.Name) if tmpDecoder != nil { - state = append(state, childDecoder) - names = append(names, currentName) + stack = append(stack, stackElement{tmpDecoder, tp.Name, i}) currentName = tp.Name currentDecoder = tmpDecoder err := currentDecoder.Start(*(*[]spec.XMLAttr)(unsafe.Pointer(&tp.Attr))) if err != nil { - for i := len(state) - 1; i >= 0; i-- { - err = state[i].WrapError(err) + for j := len(stack) - 1; j >= 0; j-- { + element := stack[j] + err = specerr.WrapIndex(err, element.name.Local, element.i) } specerr.Append(&errs, err) } @@ -105,8 +110,12 @@ func decodeModelFile(ctx context.Context, r io.Reader, model *Model, path string x.OnEnd = func(tp xml.EndElement) { if currentName == tp.Name { currentDecoder.End() - currentDecoder, state = state[len(state)-1], state[:len(state)-1] - currentName, names = names[len(names)-1], names[:len(names)-1] + stack = stack[:len(stack)-1] + if len(stack) > 0 { + element := stack[len(stack)-1] + currentDecoder = element.decoder + currentName = element.name + } } else if appendDecoder, ok := currentDecoder.(spec.AppendTokenElementDecoder); ok { appendDecoder.AppendToken(tp) } diff --git a/read_test.go b/read_test.go index 5bf29a1..444d611 100644 --- a/read_test.go +++ b/read_test.go @@ -75,6 +75,11 @@ func (f *fakeAsset) Identify() uint32 { return f.ID } +// XMLName returns the xml identifier of the resource. +func (fakeAsset) XMLName() xml.Name { + return xml.Name{Space: Namespace, Local: "fake"} +} + type fakeAttr struct { Value string } @@ -641,17 +646,17 @@ func TestNewDecoder(t *testing.T) { func TestDecoder_processRootModel_warns(t *testing.T) { spec.Register(fakeSpec.Namespace, new(qmExtension)) want := []string{ - fmt.Sprintf("Resources@BaseMaterials#0@Base#0: %v", specerr.NewParseAttrError("displaycolor", true)), - fmt.Sprintf("Resources@BaseMaterials#1: %v", specerr.NewParseAttrError("id", true)), - fmt.Sprintf("Resources@Object#0@Mesh@Vertices@Point3D#8: %v", specerr.NewParseAttrError("x", true)), - fmt.Sprintf("Resources@Object#0@Mesh@Triangles@Triangle#13: %v", specerr.NewParseAttrError("v1", true)), - fmt.Sprintf("Resources@Object#1: %v", specerr.NewParseAttrError("pid", false)), - fmt.Sprintf("Resources@Object#1: %v", specerr.NewParseAttrError("pindex", false)), - fmt.Sprintf("Resources@Object#1: %v", specerr.NewParseAttrError("type", false)), - fmt.Sprintf("Resources@Object#2@Components@Component#0: %v", specerr.NewParseAttrError("transform", false)), - fmt.Sprintf("Resources@Object#2@Components@Component#1: %v", specerr.NewParseAttrError("objectid", true)), - fmt.Sprintf("Build@Item#0: %v", specerr.NewParseAttrError("transform", false)), - fmt.Sprintf("Build@Item#3: %v", specerr.NewParseAttrError("objectid", true)), + fmt.Sprintf("model@resources@basematerials#0@base#0: %v", specerr.NewParseAttrError("displaycolor", true)), + fmt.Sprintf("model@resources@basematerials#1: %v", specerr.NewParseAttrError("id", true)), + fmt.Sprintf("model@resources@object#0@mesh@vertices@vertex#8: %v", specerr.NewParseAttrError("x", true)), + fmt.Sprintf("model@resources@object#0@mesh@triangles@triangle#13: %v", specerr.NewParseAttrError("v1", true)), + fmt.Sprintf("model@resources@object#1: %v", specerr.NewParseAttrError("pid", false)), + fmt.Sprintf("model@resources@object#1: %v", specerr.NewParseAttrError("pindex", false)), + fmt.Sprintf("model@resources@object#1: %v", specerr.NewParseAttrError("type", false)), + fmt.Sprintf("model@resources@object#2@components@component#0: %v", specerr.NewParseAttrError("transform", false)), + fmt.Sprintf("model@resources@object#2@components@component#1: %v", specerr.NewParseAttrError("objectid", true)), + fmt.Sprintf("model@build@item#0: %v", specerr.NewParseAttrError("transform", false)), + fmt.Sprintf("model@build@item#3: %v", specerr.NewParseAttrError("objectid", true)), } got := new(Model) got.Extensions = append(got.Extensions, fakeSpec) diff --git a/slices/decoder.go b/slices/decoder.go index dc76a44..e07ba2b 100644 --- a/slices/decoder.go +++ b/slices/decoder.go @@ -57,16 +57,14 @@ func (d *sliceStackDecoder) End() { d.resources.Assets = append(d.resources.Assets, &d.resource) } -func (d *sliceStackDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, d.resource, len(d.resources.Assets)) -} - -func (d *sliceStackDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *sliceStackDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace { if name.Local == attrSlice { child = &sliceDecoder{resource: &d.resource} + i = len(d.resource.Slices) } else if name.Local == attrSliceRef { child = &sliceRefDecoder{resource: &d.resource} + i = len(d.resource.Refs) } } return @@ -90,10 +88,7 @@ func (d *sliceStackDecoder) Start(attrs []spec.XMLAttr) error { d.resource.BottomZ = float32(val) } } - if errs != nil { - return specerr.WrapIndex(errs, d.resource, len(d.resources.Assets)) - } - return nil + return errs } type sliceRefDecoder struct { @@ -121,10 +116,7 @@ func (d *sliceRefDecoder) Start(attrs []spec.XMLAttr) error { } ref := SliceRef{SliceStackID: sliceStackID, Path: path} d.resource.Refs = append(d.resource.Refs, ref) - if errs != nil { - return specerr.WrapIndex(errs, ref, len(d.resource.Refs)-1) - } - return nil + return errs } type sliceDecoder struct { @@ -139,16 +131,14 @@ func (d *sliceDecoder) End() { d.resource.Slices = append(d.resource.Slices, d.slice) } -func (d *sliceDecoder) WrapError(err error) error { - return specerr.WrapIndex(err, &d.slice, len(d.resource.Slices)) -} - -func (d *sliceDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *sliceDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace { if name.Local == attrVertices { child = &d.polygonVerticesDecoder + i = -1 } else if name.Local == attrPolygon { child = &d.polygonDecoder + i = len(d.slice.Polygons) } } return @@ -168,10 +158,7 @@ func (d *sliceDecoder) Start(attrs []spec.XMLAttr) error { break } } - if errs != nil { - return specerr.WrapIndex(errs, &d.slice, len(d.resource.Slices)) - } - return nil + return errs } type polygonVerticesDecoder struct { @@ -185,17 +172,14 @@ func (d *polygonVerticesDecoder) Start(_ []spec.XMLAttr) error { return nil } -func (d *polygonVerticesDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *polygonVerticesDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrVertex { child = &d.polygonVertexDecoder + i = len(d.slice.Vertices.Vertex) } return } -func (d *polygonVerticesDecoder) WrapError(err error) error { - return specerr.Wrap(err, &d.slice.Vertices) -} - type polygonVertexDecoder struct { baseDecoder slice *Slice @@ -219,10 +203,7 @@ func (d *polygonVertexDecoder) Start(attrs []spec.XMLAttr) error { } } d.slice.Vertices.Vertex = append(d.slice.Vertices.Vertex, p) - if errs != nil { - return specerr.WrapIndex(errs, p, len(d.slice.Vertices.Vertex)-1) - } - return nil + return errs } type polygonDecoder struct { @@ -231,14 +212,10 @@ type polygonDecoder struct { polygonSegmentDecoder polygonSegmentDecoder } -func (d *polygonDecoder) WrapError(err error) error { - index := len(d.slice.Polygons) - 1 - return specerr.WrapIndex(err, &d.slice.Polygons[index], index) -} - -func (d *polygonDecoder) Child(name xml.Name) (child spec.ElementDecoder) { +func (d *polygonDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrSegment { child = &d.polygonSegmentDecoder + i = len(d.slice.Polygons) } return } @@ -258,10 +235,7 @@ func (d *polygonDecoder) Start(attrs []spec.XMLAttr) error { break } } - if errs != nil { - return specerr.WrapIndex(errs, d.slice.Polygons[polygonIndex], polygonIndex) - } - return nil + return errs } type polygonSegmentDecoder struct { @@ -299,10 +273,7 @@ func (d *polygonSegmentDecoder) Start(attrs []spec.XMLAttr) error { } } d.polygon.Segments = append(d.polygon.Segments, segment) - if errs != nil { - return specerr.WrapIndex(errs, segment, len(d.polygon.Segments)-1) - } - return nil + return errs } type baseDecoder struct { diff --git a/slices/slices.go b/slices/slices.go index d681e40..e0bd1f2 100644 --- a/slices/slices.go +++ b/slices/slices.go @@ -4,6 +4,7 @@ package slices import ( + "encoding/xml" "errors" "github.com/hpinc/go3mf" @@ -113,6 +114,11 @@ func (s *SliceStack) Identify() uint32 { return s.ID } +// XMLName returns the xml identifier of the resource. +func (SliceStack) XMLName() xml.Name { + return xml.Name{Space: Namespace, Local: attrSliceStack} +} + func GetObjectAttr(obj *go3mf.Object) *ObjectAttr { for _, a := range obj.AnyAttr { if a, ok := a.(*ObjectAttr); ok { diff --git a/slices/validate.go b/slices/validate.go index 5d098bc..1a5164d 100644 --- a/slices/validate.go +++ b/slices/validate.go @@ -87,31 +87,31 @@ func (r *SliceStack) validateSlices() error { lastTopZ := float32(-math.MaxFloat32) for j, slice := range r.Slices { if slice.TopZ == 0 { - errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrZTop), slice, j)) + errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrZTop), attrSlice, j)) } else if slice.TopZ < r.BottomZ { - errs = errors.Append(errs, errors.WrapIndex(ErrSliceSmallTopZ, slice, j)) + errs = errors.Append(errs, errors.WrapIndex(ErrSliceSmallTopZ, attrSlice, j)) } if slice.TopZ <= lastTopZ { - errs = errors.Append(errs, errors.WrapIndex(ErrSliceNoMonotonic, slice, j)) + errs = errors.Append(errs, errors.WrapIndex(ErrSliceNoMonotonic, attrSlice, j)) } lastTopZ = slice.TopZ if len(slice.Polygons) == 0 && len(slice.Vertices.Vertex) == 0 { continue } if len(slice.Vertices.Vertex) < 2 { - errs = errors.Append(errs, errors.WrapIndex(ErrSliceInsufficientVertices, slice, j)) + errs = errors.Append(errs, errors.WrapIndex(ErrSliceInsufficientVertices, attrSlice, j)) } if len(slice.Polygons) == 0 { - errs = errors.Append(errs, errors.WrapIndex(ErrSliceInsufficientPolygons, slice, j)) + errs = errors.Append(errs, errors.WrapIndex(ErrSliceInsufficientPolygons, attrSlice, j)) } var perrs error for k, p := range slice.Polygons { if len(p.Segments) < 1 { - perrs = errors.Append(perrs, errors.WrapIndex(ErrSliceInsufficientSegments, p, k)) + perrs = errors.Append(perrs, errors.WrapIndex(ErrSliceInsufficientSegments, attrPolygon, k)) } } if perrs != nil { - errs = errors.Append(errs, errors.WrapIndex(perrs, slice, j)) + errs = errors.Append(errs, errors.WrapIndex(perrs, attrSlice, j)) } } return errs @@ -124,14 +124,14 @@ func (r *SliceStack) validateRefs(m *go3mf.Model, path string) error { valid := true if ref.Path == "" { valid = false - errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrSlicePath), ref, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrSlicePath), attrSliceRef, i)) } else if ref.Path == path { valid = false - errs = errors.Append(errs, errors.WrapIndex(ErrSliceRefSamePart, ref, i)) + errs = errors.Append(errs, errors.WrapIndex(ErrSliceRefSamePart, attrSliceRef, i)) } if ref.SliceStackID == 0 { valid = false - errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrSliceRefID), ref, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrSliceRefID), attrSliceRef, i)) } if !valid { continue @@ -139,19 +139,19 @@ func (r *SliceStack) validateRefs(m *go3mf.Model, path string) error { if st, ok := m.FindAsset(ref.Path, ref.SliceStackID); ok { if st, ok := st.(*SliceStack); ok { if len(st.Refs) != 0 { - errs = errors.Append(errs, errors.WrapIndex(ErrSliceRefRef, ref, i)) + errs = errors.Append(errs, errors.WrapIndex(ErrSliceRefRef, attrSliceRef, i)) } if len(st.Slices) > 0 && st.Slices[0].TopZ <= lastTopZ { - errs = errors.Append(errs, errors.WrapIndex(ErrSliceNoMonotonic, ref, i)) + errs = errors.Append(errs, errors.WrapIndex(ErrSliceNoMonotonic, attrSliceRef, i)) } if len(st.Slices) > 0 { lastTopZ = st.Slices[len(st.Slices)-1].TopZ } } else { - errs = errors.Append(errs, errors.WrapIndex(ErrNonSliceStack, ref, i)) + errs = errors.Append(errs, errors.WrapIndex(ErrNonSliceStack, attrSliceRef, i)) } } else { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrMissingResource, ref, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrMissingResource, attrSliceRef, i)) } } return errs diff --git a/spec.go b/spec.go deleted file mode 100644 index 151303a..0000000 --- a/spec.go +++ /dev/null @@ -1,23 +0,0 @@ -// © Copyright 2021 HP Development Company, L.P. -// SPDX-License Identifier: BSD-2-Clause - -package go3mf - -import ( - "github.com/hpinc/go3mf/spec" -) - -type objectPather interface { - ObjectPath() string -} - -// UnknownAsset wraps a spec.UnknownTokens to fulfill -// the Asset interface. -type UnknownAsset struct { - spec.UnknownTokens - id uint32 -} - -func (u UnknownAsset) Identify() uint32 { - return u.id -} diff --git a/spec/encoding.go b/spec/encoding.go index 5902a08..c98ea98 100644 --- a/spec/encoding.go +++ b/spec/encoding.go @@ -27,8 +27,7 @@ type ElementDecoder interface { // that need decoding nested elements. type ChildElementDecoder interface { ElementDecoder - Child(xml.Name) ElementDecoder - WrapError(error) error + Child(xml.Name) (int, ElementDecoder) } // CharDataElementDecoder must be implemented by element decoders diff --git a/spec/spec.go b/spec/spec.go index a5c256d..6d319c9 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -52,7 +52,6 @@ type Spec interface { // Validate and aggregate the resulting erros. // // model is guaranteed to be a *go3mf.Model -// element can be a *go3mf.Model, go3mf.Asset or *go3mf.Object. type ValidateSpec interface { Spec Validate(model interface{}, path string, element interface{}) error diff --git a/spec/unknown.go b/spec/unknown.go index 6bda1b1..430c78b 100644 --- a/spec/unknown.go +++ b/spec/unknown.go @@ -30,6 +30,15 @@ func (u *UnknownAttrs) Unmarshal3MFAttr(a XMLAttr) error { // that cannot be decoded by any loaded Spec. type UnknownTokens []xml.Token +// XMLName returns the xml identifier of the resource. +func (u UnknownTokens) XMLName() xml.Name { + if len(u) == 0 { + return xml.Name{} + } + start, _ := u[0].(xml.StartElement) + return start.Name +} + func (u UnknownTokens) Marshal3MF(enc Encoder, _ *xml.StartElement) error { for _, t := range u { enc.EncodeToken(t) diff --git a/validate.go b/validate.go index 954f7e6..6e2b55c 100644 --- a/validate.go +++ b/validate.go @@ -55,16 +55,16 @@ func (m *Model) Validate() error { c := m.Childs[path] err := c.Resources.validate(m, path) if err != nil { - errs = errors.Append(errs, errors.WrapPath(err, c.Resources, path)) + errs = errors.Append(errs, errors.WrapPath(err, attrResources, path)) } } err := m.Resources.validate(m, rootPath) if err != nil { - errs = errors.Append(errs, errors.Wrap(err, m.Resources)) + errs = errors.Append(errs, errors.Wrap(err, attrResources)) } err = m.Build.validate(m) if err != nil { - errs = errors.Append(errs, errors.Wrap(err, m.Build)) + errs = errors.Append(errs, errors.Wrap(err, attrBuild)) } return errs } @@ -89,7 +89,7 @@ func (b *Build) validate(m *Model) error { for i, item := range b.Items { err := item.validate(m) if err != nil { - errs = errors.Append(errs, errors.WrapIndex(err, item, i)) + errs = errors.Append(errs, errors.WrapIndex(err, attrItem, i)) } } return errs @@ -131,9 +131,9 @@ func checkMetadadata(model *Model, md []Metadata) error { names := make(map[xml.Name]struct{}) for i, m := range md { err := m.validate(model) - errs = errors.Append(errs, errors.WrapIndex(err, m, i)) + errs = errors.Append(errs, errors.WrapIndex(err, attrMetadata, i)) if _, ok := names[m.Name]; ok { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrMetadataDuplicated, m, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrMetadataDuplicated, attrMetadata, i)) } names[m.Name] = struct{}{} } @@ -151,10 +151,10 @@ func (r *BaseMaterials) Validate(m *Model, path string) error { } for j, b := range r.Materials { if b.Name == "" { - errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrName), b, j)) + errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrName), attrBase, j)) } if b.Color == (color.RGBA{}) { - errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrDisplayColor), b, j)) + errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrDisplayColor), attrBase, j)) } } return errs @@ -182,17 +182,17 @@ func (res *Resources) validate(m *Model, path string) error { aErrs = errors.Append(aErrs, ext.Validate(m, path, r)) } } - errs = errors.Append(errs, errors.WrapIndex(aErrs, r, i)) + errs = errors.Append(errs, errors.WrapIndex(aErrs, r.XMLName().Local, i)) } for i, r := range res.Objects { if r.ID != 0 { if _, ok := assets[r.ID]; ok { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrDuplicatedID, r, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrDuplicatedID, attrObject, i)) } } assets[r.ID] = struct{}{} err := r.Validate(m, path) - errs = errors.Append(errs, errors.WrapIndex(err, r, i)) + errs = errors.Append(errs, errors.WrapIndex(err, attrObject, i)) } return errs } @@ -225,7 +225,7 @@ func (r *Object) Validate(m *Model, path string) error { } err := r.validateMesh(m, path) if err != nil { - errs = errors.Append(errs, errors.Wrap(err, r.Mesh)) + errs = errors.Append(errs, errors.Wrap(err, attrMesh)) } } if r.Components != nil && len(r.Components.Component) > 0 { @@ -257,27 +257,27 @@ func (r *Object) validateMesh(m *Model, path string) error { } nodeCount := uint32(len(r.Mesh.Vertices.Vertex)) - for i, face := range r.Mesh.Triangles.Triangle { - if face.V1 == face.V2 || face.V1 == face.V3 || face.V2 == face.V3 { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrDuplicatedIndices, face, i)) + for i, t := range r.Mesh.Triangles.Triangle { + if t.V1 == t.V2 || t.V1 == t.V3 || t.V2 == t.V3 { + errs = errors.Append(errs, errors.WrapIndex(errors.ErrDuplicatedIndices, attrTriangle, i)) } - if face.V1 >= nodeCount || face.V2 >= nodeCount || face.V3 >= nodeCount { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, face, i)) + if t.V1 >= nodeCount || t.V2 >= nodeCount || t.V3 >= nodeCount { + errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, attrTriangle, i)) } - if face.PID != 0 { - if face.PID == r.PID && face.P1 == r.PIndex && - face.P2 == r.PIndex && face.P3 == r.PIndex { + if t.PID != 0 { + if t.PID == r.PID && t.P1 == r.PIndex && + t.P2 == r.PIndex && t.P3 == r.PIndex { continue } - if a, ok := res.FindAsset(face.PID); ok { + if a, ok := res.FindAsset(t.PID); ok { if a, ok := a.(spec.PropertyGroup); ok { l := a.Len() - if int(face.P1) >= l || int(face.P2) >= l || int(face.P3) >= l { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, face, i)) + if int(t.P1) >= l || int(t.P2) >= l || int(t.P3) >= l { + errs = errors.Append(errs, errors.WrapIndex(errors.ErrIndexOutOfBounds, attrTriangle, i)) } } } else { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrMissingResource, face, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrMissingResource, attrTriangle, i)) } } } @@ -288,17 +288,17 @@ func (r *Object) validateComponents(m *Model, path string) error { var errs error for j, c := range r.Components.Component { if c.ObjectID == 0 { - errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrObjectID), c, j)) + errs = errors.Append(errs, errors.WrapIndex(errors.NewMissingFieldError(attrObjectID), attrComponent, j)) } else if ref, ok := m.FindObject(c.ObjectPath(path), c.ObjectID); ok { if ref.ID == r.ID && c.ObjectPath(path) == path { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrRecursion, c, j)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrRecursion, attrComponent, j)) } } else { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrMissingResource, c, j)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrMissingResource, attrComponent, j)) } } if errs != nil { - return errors.Wrap(errs, r.Components) + return errors.Wrap(errs, attrComponents) } return nil } @@ -321,13 +321,13 @@ func validateRelationship(m *Model, rels []Relationship, path string) error { var hasPrintTicket bool for i, r := range rels { if r.Path == "" || r.Path[0] != '/' || strings.Contains(r.Path, "/.") { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCPartName, r, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCPartName, "relationship", i)) } else { if _, ok := findAttachment(m.Attachments, r.Path); !ok { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCRelTarget, r, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCRelTarget, "relationship", i)) } if _, ok := visitedParts[partrel{r.Path, r.Type}]; ok { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCDuplicatedRel, r, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCDuplicatedRel, "relationship", i)) } visitedParts[partrel{r.Path, r.Type}] = struct{}{} } @@ -335,10 +335,10 @@ func validateRelationship(m *Model, rels []Relationship, path string) error { case RelTypePrintTicket: if a, ok := findAttachment(m.Attachments, r.Path); ok { if a.ContentType != ContentTypePrintTicket { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCContentType, r, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCContentType, "relationship", i)) } if hasPrintTicket { - errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCDuplicatedTicket, r, i)) + errs = errors.Append(errs, errors.WrapIndex(errors.ErrOPCDuplicatedTicket, "relationship", i)) } hasPrintTicket = true } @@ -379,7 +379,7 @@ func (m *Model) ValidateCoherency() error { err := r.Mesh.ValidateCoherency() if err != nil { mu.Lock() - errs = errors.Append(errs, errors.Wrap(errors.WrapIndex(errors.Wrap(err, r.Mesh), r, i), m.Resources)) + errs = errors.Append(errs, errors.Wrap(errors.WrapIndex(errors.Wrap(err, attrMesh), attrObject, i), attrResources)) mu.Unlock() } } @@ -396,7 +396,7 @@ func (m *Model) ValidateCoherency() error { err := r.Mesh.ValidateCoherency() if err != nil { mu.Lock() - errs = errors.Append(errs, errors.WrapPath(errors.WrapIndex(errors.Wrap(err, r.Mesh), r, i), res, path)) + errs = errors.Append(errs, errors.WrapPath(errors.WrapIndex(errors.Wrap(err, attrMesh), attrObject, i), attrResources, path)) mu.Unlock() } } diff --git a/validate_test.go b/validate_test.go index c9dec2d..ab59d70 100644 --- a/validate_test.go +++ b/validate_test.go @@ -27,15 +27,15 @@ func TestValidate(t *testing.T) { {}, {Path: "/.png"}, {Path: "/a.png"}, {Path: "a.png"}, {Path: "/b.png"}, {Path: "/a.png"}, {Path: "/a.png", Type: RelTypePrintTicket}, {Path: "/a.png", Type: RelTypePrintTicket}, }}, []string{ - fmt.Sprintf("/3D/3dmodel.model@Relationship#0: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model@Relationship#1: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model@Relationship#3: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model@Relationship#4: %v", errors.ErrOPCRelTarget), - fmt.Sprintf("/3D/3dmodel.model@Relationship#5: %v", errors.ErrOPCDuplicatedRel), - fmt.Sprintf("/3D/3dmodel.model@Relationship#6: %v", errors.ErrOPCContentType), - fmt.Sprintf("/3D/3dmodel.model@Relationship#7: %v", errors.ErrOPCDuplicatedRel), - fmt.Sprintf("/3D/3dmodel.model@Relationship#7: %v", errors.ErrOPCContentType), - fmt.Sprintf("/3D/3dmodel.model@Relationship#7: %v", errors.ErrOPCDuplicatedTicket), + fmt.Sprintf("/3D/3dmodel.model@relationship#0: %v", errors.ErrOPCPartName), + fmt.Sprintf("/3D/3dmodel.model@relationship#1: %v", errors.ErrOPCPartName), + fmt.Sprintf("/3D/3dmodel.model@relationship#3: %v", errors.ErrOPCPartName), + fmt.Sprintf("/3D/3dmodel.model@relationship#4: %v", errors.ErrOPCRelTarget), + fmt.Sprintf("/3D/3dmodel.model@relationship#5: %v", errors.ErrOPCDuplicatedRel), + fmt.Sprintf("/3D/3dmodel.model@relationship#6: %v", errors.ErrOPCContentType), + fmt.Sprintf("/3D/3dmodel.model@relationship#7: %v", errors.ErrOPCDuplicatedRel), + fmt.Sprintf("/3D/3dmodel.model@relationship#7: %v", errors.ErrOPCContentType), + fmt.Sprintf("/3D/3dmodel.model@relationship#7: %v", errors.ErrOPCDuplicatedTicket), }}, {"namespaces", &Model{Extensions: []Extension{{Namespace: "fake", LocalName: "f", IsRequired: true}}}, []string{ errors.ErrRequiredExt.Error(), @@ -43,10 +43,10 @@ func TestValidate(t *testing.T) { {"metadata", &Model{Extensions: []Extension{{Namespace: "fake", LocalName: "f"}}, Metadata: []Metadata{ {Name: xml.Name{Space: "fake", Local: "issue"}}, {Name: xml.Name{Space: "f", Local: "issue"}}, {Name: xml.Name{Space: "fake", Local: "issue"}}, {Name: xml.Name{Local: "issue"}}, {}, }}, []string{ - fmt.Sprintf("Metadata#1: %v", errors.ErrMetadataNamespace), - fmt.Sprintf("Metadata#2: %v", errors.ErrMetadataDuplicated), - fmt.Sprintf("Metadata#3: %v", errors.ErrMetadataName), - fmt.Sprintf("Metadata#4: %v", &errors.MissingFieldError{Name: attrName}), + fmt.Sprintf("metadata#1: %v", errors.ErrMetadataNamespace), + fmt.Sprintf("metadata#2: %v", errors.ErrMetadataDuplicated), + fmt.Sprintf("metadata#3: %v", errors.ErrMetadataName), + fmt.Sprintf("metadata#4: %v", &errors.MissingFieldError{Name: attrName}), }}, {"build", &Model{Resources: Resources{Assets: []Asset{&BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}}, Objects: []*Object{ {ID: 2, Type: ObjectTypeOther, Mesh: &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, Triangles: Triangles{Triangle: []Triangle{ @@ -58,30 +58,30 @@ func TestValidate(t *testing.T) { {ObjectID: 1, Metadata: MetadataGroup{Metadata: []Metadata{{Name: xml.Name{Local: "issue"}}}}}, }}}, []string{ "Build: fake", - fmt.Sprintf("Build@Item#0: %v", &errors.MissingFieldError{Name: attrObjectID}), - fmt.Sprintf("Build@Item#1: %v", errors.ErrOtherItem), - fmt.Sprintf("Build@Item#2: %v", errors.ErrMissingResource), - fmt.Sprintf("Build@Item#3: %v", errors.ErrMissingResource), - fmt.Sprintf("Build@Item#3@Metadata#0: %v", errors.ErrMetadataName), + fmt.Sprintf("build@item#0: %v", &errors.MissingFieldError{Name: attrObjectID}), + fmt.Sprintf("build@item#1: %v", errors.ErrOtherItem), + fmt.Sprintf("build@item#2: %v", errors.ErrMissingResource), + fmt.Sprintf("build@item#3: %v", errors.ErrMissingResource), + fmt.Sprintf("build@item#3@metadata#0: %v", errors.ErrMetadataName), }}, {"childs", &Model{Childs: map[string]*ChildModel{DefaultModelPath: {}, "/a.model": { Relationships: make([]Relationship, 1), Resources: Resources{Objects: []*Object{{}}}}}}, []string{ errors.ErrOPCDuplicatedModelName.Error(), - fmt.Sprintf("/a.model@Relationship#0: %v", errors.ErrOPCPartName), - fmt.Sprintf("/a.model@Resources@Object#0: %v", errors.ErrMissingID), - fmt.Sprintf("/a.model@Resources@Object#0: %v", errors.ErrInvalidObject), + fmt.Sprintf("/a.model@relationship#0: %v", errors.ErrOPCPartName), + fmt.Sprintf("/a.model@resources@object#0: %v", errors.ErrMissingID), + fmt.Sprintf("/a.model@resources@object#0: %v", errors.ErrInvalidObject), }}, {"assets", &Model{Resources: Resources{Assets: []Asset{ &BaseMaterials{Materials: []Base{{Color: color.RGBA{}}}}, &BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}, &BaseMaterials{ID: 1}, }}}, []string{ - fmt.Sprintf("Resources@BaseMaterials#0: %v", errors.ErrMissingID), - fmt.Sprintf("Resources@BaseMaterials#0@Base#0: %v", &errors.MissingFieldError{Name: attrName}), - fmt.Sprintf("Resources@BaseMaterials#0@Base#0: %v", &errors.MissingFieldError{Name: attrDisplayColor}), - fmt.Sprintf("Resources@BaseMaterials#2: %v", errors.ErrDuplicatedID), - fmt.Sprintf("Resources@BaseMaterials#2: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("resources@basematerials#0: %v", errors.ErrMissingID), + fmt.Sprintf("resources@basematerials#0@base#0: %v", &errors.MissingFieldError{Name: attrName}), + fmt.Sprintf("resources@basematerials#0@base#0: %v", &errors.MissingFieldError{Name: attrDisplayColor}), + fmt.Sprintf("resources@basematerials#2: %v", errors.ErrDuplicatedID), + fmt.Sprintf("resources@basematerials#2: %v", errors.ErrEmptyResourceProps), }}, {"objects", &Model{Resources: Resources{Assets: []Asset{ &BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}, {Name: "b", Color: color.RGBA{A: 1}}}}, @@ -104,29 +104,29 @@ func TestValidate(t *testing.T) { {V1: 1, V2: 2, V3: 3, PID: 100, P1: 0, P2: 0, P3: 0}, }}}}, }}}, []string{ - fmt.Sprintf("Resources@Object#0: %v", errors.ErrMissingID), - fmt.Sprintf("Resources@Object#0: %v", errors.ErrInvalidObject), - fmt.Sprintf("Resources@Object#1: %v", errors.ErrDuplicatedID), - fmt.Sprintf("Resources@Object#1: %v", &errors.MissingFieldError{Name: attrPID}), - fmt.Sprintf("Resources@Object#1: %v", errors.ErrInvalidObject), - fmt.Sprintf("Resources@Object#1@Mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("Resources@Object#1@Mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("Resources@Object#1@Components@Component#0: %v", errors.ErrRecursion), - fmt.Sprintf("Resources@Object#3: %v", errors.ErrComponentsPID), - fmt.Sprintf("Resources@Object#3@Components@Component#0: %v", errors.ErrRecursion), - fmt.Sprintf("Resources@Object#3@Components@Component#2: %v", &errors.MissingFieldError{Name: attrObjectID}), - fmt.Sprintf("Resources@Object#3@Components@Component#3: %v", errors.ErrMissingResource), - fmt.Sprintf("Resources@Object#3@Components@Component#4: %v", errors.ErrMissingResource), - fmt.Sprintf("Resources@Object#4: %v", errors.ErrMissingResource), - fmt.Sprintf("Resources@Object#4@Mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("Resources@Object#4@Mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("Resources@Object#4@Mesh@Triangle#0: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("Resources@Object#4@Mesh@Triangle#1: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("Resources@Object#4@Mesh@Triangle#2: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("Resources@Object#5: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("Resources@Object#5@Mesh@Triangle#0: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("Resources@Object#5@Mesh@Triangle#1: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("Resources@Object#5@Mesh@Triangle#3: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@object#0: %v", errors.ErrMissingID), + fmt.Sprintf("resources@object#0: %v", errors.ErrInvalidObject), + fmt.Sprintf("resources@object#1: %v", errors.ErrDuplicatedID), + fmt.Sprintf("resources@object#1: %v", &errors.MissingFieldError{Name: attrPID}), + fmt.Sprintf("resources@object#1: %v", errors.ErrInvalidObject), + fmt.Sprintf("resources@object#1@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("resources@object#1@mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("resources@object#1@components@component#0: %v", errors.ErrRecursion), + fmt.Sprintf("resources@object#3: %v", errors.ErrComponentsPID), + fmt.Sprintf("resources@object#3@components@component#0: %v", errors.ErrRecursion), + fmt.Sprintf("resources@object#3@components@component#2: %v", &errors.MissingFieldError{Name: attrObjectID}), + fmt.Sprintf("resources@object#3@components@component#3: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@object#3@components@component#4: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@object#4: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@object#4@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("resources@object#4@mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("resources@object#4@mesh@triangle#0: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("resources@object#4@mesh@triangle#1: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("resources@object#4@mesh@triangle#2: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("resources@object#5: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("resources@object#5@mesh@triangle#0: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("resources@object#5@mesh@triangle#1: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("resources@object#5@mesh@triangle#3: %v", errors.ErrMissingResource), }}, } for _, tt := range tests { @@ -211,8 +211,8 @@ func TestModel_ValidateCoherency(t *testing.T) { }}, Childs: map[string]*ChildModel{"/other.model": {Resources: Resources{Objects: []*Object{ {Mesh: invalidMesh}, }}}}}, []string{ - fmt.Sprintf("/other.model@Resources@Object#0@Mesh: %v", errors.ErrMeshConsistency), - fmt.Sprintf("Resources@Object#0@Mesh: %v", errors.ErrMeshConsistency), + fmt.Sprintf("/other.model@resources@object#0@mesh: %v", errors.ErrMeshConsistency), + fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrMeshConsistency), }}, } for _, tt := range tests { From 7f5332a3f5d209c0f72c37cf49d70e1774362d5b Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 13 Oct 2021 11:58:32 +0200 Subject: [PATCH 19/23] simplify elemenet decoder --- beamlattice/decoder.go | 22 +++++++------- beamlattice/decoder_test.go | 22 +++++++------- beamlattice/validate_test.go | 36 +++++++++++----------- decoder.go | 18 +++++++++-- materials/decoder.go | 45 +++++++++++++--------------- materials/decoder_test.go | 16 +++++----- materials/validate_test.go | 56 +++++++++++++++++----------------- production/decoder.go | 2 +- production/decoder_test.go | 8 ++--- production/validate_test.go | 34 ++++++++++----------- read_test.go | 16 ++++++---- slices/decoder.go | 13 ++++---- slices/decoder_test.go | 20 ++++++------- slices/validate_test.go | 58 ++++++++++++++++++------------------ spec/encoding.go | 5 ++++ spec/spec.go | 2 +- 16 files changed, 195 insertions(+), 178 deletions(-) diff --git a/beamlattice/decoder.go b/beamlattice/decoder.go index 7ae8c8c..ede9196 100644 --- a/beamlattice/decoder.go +++ b/beamlattice/decoder.go @@ -7,7 +7,6 @@ import ( "encoding/xml" "strconv" - "github.com/hpinc/go3mf" specerr "github.com/hpinc/go3mf/errors" "github.com/hpinc/go3mf/spec" ) @@ -16,23 +15,24 @@ func (Spec) NewAttrGroup(xml.Name) spec.AttrGroup { return nil } -func (Spec) NewElementDecoder(parent interface{}, name string) spec.ElementDecoder { - if name == attrBeamLattice { - return &beamLatticeDecoder{mesh: parent.(*go3mf.Mesh)} +func (Spec) NewElementDecoder(name xml.Name) spec.GetterElementDecoder { + if name.Space == Namespace && name.Local == attrBeamLattice { + return new(beamLatticeDecoder) } return nil } type beamLatticeDecoder struct { baseDecoder - mesh *go3mf.Mesh - beamLattice *BeamLattice + beamLattice BeamLattice +} + +func (d *beamLatticeDecoder) Element() interface{} { + return &d.beamLattice } func (d *beamLatticeDecoder) Start(attrs []spec.XMLAttr) error { var errs error - d.beamLattice = new(BeamLattice) - d.mesh.Any = append(d.mesh.Any, d.beamLattice) for _, a := range attrs { if a.Name.Space != "" { continue @@ -82,10 +82,10 @@ func (d *beamLatticeDecoder) Start(attrs []spec.XMLAttr) error { func (d *beamLatticeDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace { if name.Local == attrBeams { - child = &beamsDecoder{beamLattice: d.beamLattice} + child = &beamsDecoder{beamLattice: &d.beamLattice} i = -1 } else if name.Local == attrBeamSets { - child = &beamSetsDecoder{beamLattice: d.beamLattice} + child = &beamSetsDecoder{beamLattice: &d.beamLattice} i = -1 } } @@ -224,7 +224,7 @@ func (d *beamSetDecoder) Start(attrs []spec.XMLAttr) error { func (d *beamSetDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { if name.Space == Namespace && name.Local == attrRef { child = &d.beamRefDecoder - i = -1 + i = len(d.beamSet.Refs) } return } diff --git a/beamlattice/decoder_test.go b/beamlattice/decoder_test.go index 39b828c..2db8cea 100644 --- a/beamlattice/decoder_test.go +++ b/beamlattice/decoder_test.go @@ -119,17 +119,17 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", errors.NewParseAttrError("radius", true)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", errors.NewParseAttrError("minlength", true)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", errors.NewParseAttrError("cap", false)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", errors.NewParseAttrError("clippingmode", false)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", errors.NewParseAttrError("clippingmesh", false)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", errors.NewParseAttrError("representationmesh", false)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beams@Beam#0: %v", errors.NewParseAttrError("r1", false)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beams@Beam#0: %v", errors.NewParseAttrError("r2", false)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beams@Beam#2: %v", errors.NewParseAttrError("v2", true)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beams@Beam#3: %v", errors.NewParseAttrError("v1", true)), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@BeamSets@BeamSet#0@uint32#2: %v", errors.NewParseAttrError("index", true)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("radius", true)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("minlength", true)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("cap", false)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("clippingmode", false)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("clippingmesh", false)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("representationmesh", false)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beams@beam#0: %v", errors.NewParseAttrError("r1", false)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beams@beam#0: %v", errors.NewParseAttrError("r2", false)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beams@beam#2: %v", errors.NewParseAttrError("v2", true)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beams@beam#3: %v", errors.NewParseAttrError("v1", true)), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beamsets@beamset#0@ref#2: %v", errors.NewParseAttrError("index", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/beamlattice/validate_test.go b/beamlattice/validate_test.go index 8470ab9..46d2ec4 100644 --- a/beamlattice/validate_test.go +++ b/beamlattice/validate_test.go @@ -24,21 +24,21 @@ func TestValidate(t *testing.T) { {ID: 1, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{}}}}, }}}, }}, []string{ - fmt.Sprintf("/other.model@Resources@Object#0@Mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("/other.model@Resources@Object#0@Mesh@BeamLattice: %v", &errors.MissingFieldError{Name: attrMinLength}), - fmt.Sprintf("/other.model@Resources@Object#0@Mesh@BeamLattice: %v", &errors.MissingFieldError{Name: attrRadius}), - fmt.Sprintf("/other.model@Resources@Object#0@Mesh@BeamLattice: %v", ErrLatticeClippedNoMesh), + fmt.Sprintf("/other.model@resources@object#0@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("/other.model@resources@object#0@mesh@beamlattice: %v", &errors.MissingFieldError{Name: attrMinLength}), + fmt.Sprintf("/other.model@resources@object#0@mesh@beamlattice: %v", &errors.MissingFieldError{Name: attrRadius}), + fmt.Sprintf("/other.model@resources@object#0@mesh@beamlattice: %v", ErrLatticeClippedNoMesh), }}, {"object without beamlattice", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Mesh: &go3mf.Mesh{}}, }}}, []string{ - fmt.Sprintf("Resources@Object#0@Mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("Resources@Object#0@Mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrInsufficientTriangles), }}, {"object with components", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 2}}}}, }}}, []string{ - fmt.Sprintf("Resources@Object#0@Components@Component#0: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@object#0@components@component#0: %v", errors.ErrMissingResource), }}, {"object incorret type", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Type: go3mf.ObjectTypeOther, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{ @@ -51,9 +51,9 @@ func TestValidate(t *testing.T) { MinLength: 1, Radius: 1, ClipMode: ClipInside, }}}}, }}}, []string{ - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice: %v", ErrLatticeObjType), - fmt.Sprintf("Resources@Object#1@Mesh@BeamLattice: %v", ErrLatticeObjType), - fmt.Sprintf("Resources@Object#2@Mesh@BeamLattice: %v", ErrLatticeObjType), + fmt.Sprintf("resources@object#0@mesh@beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("resources@object#1@mesh@beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("resources@object#2@mesh@beamlattice: %v", ErrLatticeObjType), }}, {"incorrect mesh references", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{nil}}}, @@ -64,9 +64,9 @@ func TestValidate(t *testing.T) { MinLength: 1, Radius: 1, ClippingMeshID: 1, RepresentationMeshID: 2, }}}}, }}}, []string{ - fmt.Sprintf("Resources@Object#1@Mesh@BeamLattice: %v", errors.ErrMissingResource), - fmt.Sprintf("Resources@Object#1@Mesh@BeamLattice: %v", errors.ErrRecursion), - fmt.Sprintf("Resources@Object#2@Mesh@BeamLattice: %v", ErrLatticeInvalidMesh), + fmt.Sprintf("resources@object#1@mesh@beamlattice: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@object#1@mesh@beamlattice: %v", errors.ErrRecursion), + fmt.Sprintf("resources@object#2@mesh@beamlattice: %v", ErrLatticeInvalidMesh), }}, {"incorrect beams", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ @@ -75,10 +75,10 @@ func TestValidate(t *testing.T) { }, }}}}}, }}}, []string{ - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#0: %v", ErrLatticeSameVertex), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#1: %v", ErrLatticeSameVertex), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#1: %v", ErrLatticeBeamR2), - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@Beam#2: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("resources@object#0@mesh@beamlattice@beam#0: %v", ErrLatticeSameVertex), + fmt.Sprintf("resources@object#0@mesh@beamlattice@beam#1: %v", ErrLatticeSameVertex), + fmt.Sprintf("resources@object#0@mesh@beamlattice@beam#1: %v", ErrLatticeBeamR2), + fmt.Sprintf("resources@object#0@mesh@beamlattice@beam#2: %v", errors.ErrIndexOutOfBounds), }}, {"incorrect beamseat", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ @@ -87,7 +87,7 @@ func TestValidate(t *testing.T) { }}, BeamSets: BeamSets{BeamSet: []BeamSet{{Refs: []uint32{0, 2, 3}}}}, }}}}, }}}, []string{ - fmt.Sprintf("Resources@Object#0@Mesh@BeamLattice@BeamSet#0: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("resources@object#0@mesh@beamlattice@beamset#0: %v", errors.ErrIndexOutOfBounds), }}, } for _, tt := range tests { diff --git a/decoder.go b/decoder.go index 139fbb8..cf5aca1 100644 --- a/decoder.go +++ b/decoder.go @@ -40,7 +40,11 @@ func (d *modelDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { } } } else if ext, ok := spec.Load(name.Space); ok { - child = ext.NewElementDecoder(d.model, name.Local) + dec := ext.NewElementDecoder(name) + child = dec + if dec != nil { + d.model.Any = append(d.model.Any, dec.Element().(spec.Marshaler)) + } i = -1 } else { child = spec.NewAnyUnknownDecoder(name, &d.model.Any) @@ -297,8 +301,12 @@ func (d *resourceDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder i = len(d.resources.Assets) } } else if ext, ok := spec.Load(name.Space); ok { - child = ext.NewElementDecoder(d.resources, name.Local) + dec := ext.NewElementDecoder(name) i = len(d.resources.Assets) + child = dec + if dec != nil { + d.resources.Assets = append(d.resources.Assets, dec.Element().(Asset)) + } } else { child = &unknownAssetDecoder{UnknownTokensDecoder: *spec.NewUnknownDecoder(name), resources: d.resources} i = len(d.resources.Assets) @@ -413,7 +421,11 @@ func (d *meshDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { i = -1 } } else if ext, ok := spec.Load(name.Space); ok { - child = ext.NewElementDecoder(d.resource.Mesh, name.Local) + dec := ext.NewElementDecoder(name) + child = dec + if dec != nil { + d.resource.Mesh.Any = append(d.resource.Mesh.Any, dec.Element().(spec.Marshaler)) + } i = -1 } else { child = spec.NewAnyUnknownDecoder(name, &d.resource.Mesh.Any) diff --git a/materials/decoder.go b/materials/decoder.go index 3358bac..b038fb9 100644 --- a/materials/decoder.go +++ b/materials/decoder.go @@ -8,7 +8,6 @@ import ( "strconv" "strings" - "github.com/hpinc/go3mf" specerr "github.com/hpinc/go3mf/errors" "github.com/hpinc/go3mf/spec" ) @@ -17,31 +16,33 @@ func (Spec) NewAttrGroup(xml.Name) spec.AttrGroup { return nil } -func (Spec) NewElementDecoder(parent interface{}, name string) (child spec.ElementDecoder) { - switch name { +func (Spec) NewElementDecoder(name xml.Name) (child spec.GetterElementDecoder) { + if name.Space != Namespace { + return + } + switch name.Local { case attrColorGroup: - child = &colorGroupDecoder{resources: parent.(*go3mf.Resources)} + child = new(colorGroupDecoder) case attrTexture2DGroup: - child = &tex2DGroupDecoder{resources: parent.(*go3mf.Resources)} + child = new(tex2DGroupDecoder) case attrTexture2D: - child = &texture2DDecoder{resources: parent.(*go3mf.Resources)} + child = new(texture2DDecoder) case attrCompositematerials: - child = &compositeMaterialsDecoder{resources: parent.(*go3mf.Resources)} + child = new(compositeMaterialsDecoder) case attrMultiProps: - child = &multiPropertiesDecoder{resources: parent.(*go3mf.Resources)} + child = new(multiPropertiesDecoder) } return } type colorGroupDecoder struct { baseDecoder - resources *go3mf.Resources resource ColorGroup colorDecoder colorDecoder } -func (d *colorGroupDecoder) End() { - d.resources.Assets = append(d.resources.Assets, &d.resource) +func (d *colorGroupDecoder) Element() interface{} { + return &d.resource } func (d *colorGroupDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { @@ -120,13 +121,12 @@ func (d *tex2DCoordDecoder) Start(attrs []spec.XMLAttr) error { type tex2DGroupDecoder struct { baseDecoder - resources *go3mf.Resources resource Texture2DGroup tex2DCoordDecoder tex2DCoordDecoder } -func (d *tex2DGroupDecoder) End() { - d.resources.Assets = append(d.resources.Assets, &d.resource) +func (d *tex2DGroupDecoder) Element() interface{} { + return &d.resource } func (d *tex2DGroupDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { @@ -164,12 +164,11 @@ func (d *tex2DGroupDecoder) Start(attrs []spec.XMLAttr) error { type texture2DDecoder struct { baseDecoder - resources *go3mf.Resources - resource Texture2D + resource Texture2D } -func (d *texture2DDecoder) End() { - d.resources.Assets = append(d.resources.Assets, &d.resource) +func (d *texture2DDecoder) Element() interface{} { + return &d.resource } func (d *texture2DDecoder) Start(attrs []spec.XMLAttr) error { @@ -202,13 +201,12 @@ func (d *texture2DDecoder) Start(attrs []spec.XMLAttr) error { type compositeMaterialsDecoder struct { baseDecoder - resources *go3mf.Resources resource CompositeMaterials compositeDecoder compositeDecoder } -func (d *compositeMaterialsDecoder) End() { - d.resources.Assets = append(d.resources.Assets, &d.resource) +func (d *compositeMaterialsDecoder) Element() interface{} { + return &d.resource } func (d *compositeMaterialsDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { @@ -279,13 +277,12 @@ func (d *compositeDecoder) Start(attrs []spec.XMLAttr) error { type multiPropertiesDecoder struct { baseDecoder - resources *go3mf.Resources resource MultiProperties multiDecoder multiDecoder } -func (d *multiPropertiesDecoder) End() { - d.resources.Assets = append(d.resources.Assets, &d.resource) +func (d *multiPropertiesDecoder) Element() interface{} { + return &d.resource } func (d *multiPropertiesDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { diff --git a/materials/decoder_test.go b/materials/decoder_test.go index 066d3bb..bd7dd87 100644 --- a/materials/decoder_test.go +++ b/materials/decoder_test.go @@ -63,14 +63,14 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("Resources@Texture2D#1: %v", errors.NewParseAttrError("id", true)), - fmt.Sprintf("Resources@ColorGroup#2@RGBA#0: %v", errors.NewParseAttrError("color", true)), - fmt.Sprintf("Resources@Texture2DGroup#3: %v", errors.NewParseAttrError("texid", true)), - fmt.Sprintf("Resources@Texture2DGroup#3@TextureCoord#0: %v", errors.NewParseAttrError("u", true)), - fmt.Sprintf("Resources@Texture2DGroup#3@TextureCoord#1: %v", errors.NewParseAttrError("v", true)), - fmt.Sprintf("Resources@CompositeMaterials#4: %v", errors.NewParseAttrError("matid", true)), - fmt.Sprintf("Resources@CompositeMaterials#4@Composite#1: %v", errors.NewParseAttrError("values", true)), - fmt.Sprintf("Resources@MultiProperties#5: %v", errors.NewParseAttrError("pids", true)), + fmt.Sprintf("model@resources@texture2d#1: %v", errors.NewParseAttrError("id", true)), + fmt.Sprintf("model@resources@colorgroup#2@color#0: %v", errors.NewParseAttrError("color", true)), + fmt.Sprintf("model@resources@texture2dgroup#3: %v", errors.NewParseAttrError("texid", true)), + fmt.Sprintf("model@resources@texture2dgroup#3@tex2coord#0: %v", errors.NewParseAttrError("u", true)), + fmt.Sprintf("model@resources@texture2dgroup#3@tex2coord#1: %v", errors.NewParseAttrError("v", true)), + fmt.Sprintf("model@resources@compositematerials#4: %v", errors.NewParseAttrError("matid", true)), + fmt.Sprintf("model@resources@compositematerials#4@composite#1: %v", errors.NewParseAttrError("values", true)), + fmt.Sprintf("model@resources@multiproperties#5: %v", errors.NewParseAttrError("pids", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/materials/validate_test.go b/materials/validate_test.go index 03c4cde..6b68c9d 100644 --- a/materials/validate_test.go +++ b/materials/validate_test.go @@ -27,10 +27,10 @@ func TestValidate(t *testing.T) { &MultiProperties{ID: 2}, }}}, }}, []string{ - fmt.Sprintf("/other.model@Resources@ColorGroup#0: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("/that.model@Resources@MultiProperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), - fmt.Sprintf("/that.model@Resources@MultiProperties#0: %v", ErrMultiBlend), - fmt.Sprintf("/that.model@Resources@MultiProperties#0: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("/other.model@resources@colorgroup#0: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("/that.model@resources@multiproperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), + fmt.Sprintf("/that.model@resources@multiproperties#0: %v", ErrMultiBlend), + fmt.Sprintf("/that.model@resources@multiproperties#0: %v", errors.ErrEmptyResourceProps), }}, {"multi", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -48,15 +48,15 @@ func TestValidate(t *testing.T) { &MultiProperties{ID: 9, Multis: []Multi{{PIndices: []uint32{}}}, PIDs: []uint32{1, 3}}, }}, }, []string{ - fmt.Sprintf("Resources@MultiProperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), - fmt.Sprintf("Resources@MultiProperties#0: %v", ErrMultiBlend), - fmt.Sprintf("Resources@MultiProperties#0: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("Resources@MultiProperties#1: %v", ErrMultiRefMulti), - fmt.Sprintf("Resources@MultiProperties#1: %v", errors.ErrMissingResource), - fmt.Sprintf("Resources@MultiProperties#6@Multi#0: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("Resources@MultiProperties#7: %v", ErrMaterialMulti), - fmt.Sprintf("Resources@MultiProperties#7: %v", ErrMultiColors), - fmt.Sprintf("Resources@MultiProperties#8: %v", ErrMaterialMulti), + fmt.Sprintf("resources@multiproperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), + fmt.Sprintf("resources@multiproperties#0: %v", ErrMultiBlend), + fmt.Sprintf("resources@multiproperties#0: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("resources@multiproperties#1: %v", ErrMultiRefMulti), + fmt.Sprintf("resources@multiproperties#1: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@multiproperties#6@multi#0: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("resources@multiproperties#7: %v", ErrMaterialMulti), + fmt.Sprintf("resources@multiproperties#7: %v", ErrMultiColors), + fmt.Sprintf("resources@multiproperties#8: %v", ErrMaterialMulti), }}, {"missingTextPart", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -64,9 +64,9 @@ func TestValidate(t *testing.T) { &Texture2D{ID: 2, ContentType: TextureTypePNG, Path: "/a.png"}, }}, }, []string{ - fmt.Sprintf("Resources@Texture2D#0: %v", &errors.MissingFieldError{Name: attrPath}), - fmt.Sprintf("Resources@Texture2D#0: %v", &errors.MissingFieldError{Name: attrContentType}), - fmt.Sprintf("Resources@Texture2D#1: %v", ErrMissingTexturePart), + fmt.Sprintf("resources@texture2d#0: %v", &errors.MissingFieldError{Name: attrPath}), + fmt.Sprintf("resources@texture2d#0: %v", &errors.MissingFieldError{Name: attrContentType}), + fmt.Sprintf("resources@texture2d#1: %v", ErrMissingTexturePart), }}, {"textureGroup", &go3mf.Model{ Attachments: []go3mf.Attachment{{Path: "/a.png"}}, @@ -78,10 +78,10 @@ func TestValidate(t *testing.T) { &Texture2DGroup{ID: 5, TextureID: 100, Coords: []TextureCoord{{}}}, }}, }, []string{ - fmt.Sprintf("Resources@Texture2DGroup#1: %v", &errors.MissingFieldError{Name: attrTexID}), - fmt.Sprintf("Resources@Texture2DGroup#1: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("Resources@Texture2DGroup#3: %v", ErrTextureReference), - fmt.Sprintf("Resources@Texture2DGroup#4: %v", ErrTextureReference), + fmt.Sprintf("resources@texture2dgroup#1: %v", &errors.MissingFieldError{Name: attrTexID}), + fmt.Sprintf("resources@texture2dgroup#1: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("resources@texture2dgroup#3: %v", ErrTextureReference), + fmt.Sprintf("resources@texture2dgroup#4: %v", ErrTextureReference), }}, {"colorGroup", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -90,8 +90,8 @@ func TestValidate(t *testing.T) { &ColorGroup{ID: 3, Colors: []color.RGBA{{R: 1}, {}}}, }}, }, []string{ - fmt.Sprintf("Resources@ColorGroup#0: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("Resources@ColorGroup#2@RGBA#1: %v", &errors.MissingFieldError{Name: attrColor}), + fmt.Sprintf("resources@colorgroup#0: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("resources@colorgroup#2@color#1: %v", &errors.MissingFieldError{Name: attrColor}), }}, {"composite", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -105,12 +105,12 @@ func TestValidate(t *testing.T) { &CompositeMaterials{ID: 5, MaterialID: 2, Indices: []uint32{0, 1}, Composites: []Composite{{Values: []float32{1, 2}}}}, &CompositeMaterials{ID: 6, MaterialID: 100, Indices: []uint32{0, 1}, Composites: []Composite{{Values: []float32{1, 2}}}}, }}}, []string{ - fmt.Sprintf("Resources@CompositeMaterials#1: %v", &errors.MissingFieldError{Name: attrMatID}), - fmt.Sprintf("Resources@CompositeMaterials#1: %v", &errors.MissingFieldError{Name: attrMatIndices}), - fmt.Sprintf("Resources@CompositeMaterials#1: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("Resources@CompositeMaterials#3: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("Resources@CompositeMaterials#4: %v", ErrCompositeBase), - fmt.Sprintf("Resources@CompositeMaterials#5: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@compositematerials#1: %v", &errors.MissingFieldError{Name: attrMatID}), + fmt.Sprintf("resources@compositematerials#1: %v", &errors.MissingFieldError{Name: attrMatIndices}), + fmt.Sprintf("resources@compositematerials#1: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("resources@compositematerials#3: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("resources@compositematerials#4: %v", ErrCompositeBase), + fmt.Sprintf("resources@compositematerials#5: %v", errors.ErrMissingResource), }}, } for _, tt := range tests { diff --git a/production/decoder.go b/production/decoder.go index 7fa2865..66ba0d0 100644 --- a/production/decoder.go +++ b/production/decoder.go @@ -12,7 +12,7 @@ import ( "github.com/hpinc/go3mf/uuid" ) -func (Spec) NewElementDecoder(_ interface{}, _ string) spec.ElementDecoder { +func (Spec) NewElementDecoder(xml.Name) spec.GetterElementDecoder { return nil } diff --git a/production/decoder_test.go b/production/decoder_test.go index 80b4495..7708820 100644 --- a/production/decoder_test.go +++ b/production/decoder_test.go @@ -73,10 +73,10 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("Resources@Object#1: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), - fmt.Sprintf("Resources@Object#1@Components@Component#0: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), - fmt.Sprintf("Build: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), - fmt.Sprintf("Build@Item#0: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("model@resources@object#1: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("model@resources@object#1@components@component#0: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("model@build: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("model@build@item#0: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/production/validate_test.go b/production/validate_test.go index a370486..5047922 100644 --- a/production/validate_test.go +++ b/production/validate_test.go @@ -23,15 +23,15 @@ func TestValidate(t *testing.T) { want []string }{ {"buildNoUUID", &go3mf.Model{Build: go3mf.Build{}}, []string{ - fmt.Sprintf("Build: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("build: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"buildEmptyUUID", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{}}}}, []string{ - fmt.Sprintf("Build: %v", ErrUUID), + fmt.Sprintf("build: %v", ErrUUID), }}, {"buildNonValidUUID", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{"a-b-c-d"}}}}, []string{ - fmt.Sprintf("Build: %v", ErrUUID), + fmt.Sprintf("build: %v", ErrUUID), }}, {"extReq", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, @@ -43,7 +43,7 @@ func TestValidate(t *testing.T) { AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ {ObjectID: 1, AnyAttr: spec.AnyAttr{&ItemAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d478", Path: "/other.model"}}}, }}}, []string{ - fmt.Sprintf("/other.model@Resources@Object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"items", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ @@ -54,11 +54,11 @@ func TestValidate(t *testing.T) { }}, Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, Resources: go3mf.Resources{Objects: []*go3mf.Object{{ID: 1, Mesh: validMesh.Mesh}}}}, []string{ - fmt.Sprintf("Build@Item#1: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("Build@Item#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("Build@Item#3: %v", ErrUUID), - fmt.Sprintf("/other.model@Resources@Object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("Resources@Object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("build@item#1: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("build@item#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("build@item#3: %v", ErrUUID), + fmt.Sprintf("/other.model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"components", &go3mf.Model{Resources: go3mf.Resources{ Objects: []*go3mf.Object{ @@ -70,10 +70,10 @@ func TestValidate(t *testing.T) { }}}, }, }, Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}}, []string{ - fmt.Sprintf("Resources@Object#0: %v", ErrUUID), - fmt.Sprintf("Resources@Object#1@Components@Component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("Resources@Object#1@Components@Component#1: %v", ErrUUID), - fmt.Sprintf("Resources@Object#1@Components@Component#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("resources@object#0: %v", ErrUUID), + fmt.Sprintf("resources@object#1@components@component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("resources@object#1@components@component#1: %v", ErrUUID), + fmt.Sprintf("resources@object#1@components@component#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"child", &go3mf.Model{Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}, Childs: map[string]*go3mf.ChildModel{ @@ -83,10 +83,10 @@ func TestValidate(t *testing.T) { {ObjectID: 1, AnyAttr: spec.AnyAttr{&ComponentAttr{Path: "/b.model"}}}, }}}, }}}}}, []string{ - fmt.Sprintf("/b.model@Resources@Object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model@Resources@Object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model@Resources@Object#0@Components@Component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model@Resources@Object#0@Components@Component#0: %v", ErrProdRefInNonRoot), + fmt.Sprintf("/b.model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model@resources@object#0@components@component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model@resources@object#0@components@component#0: %v", ErrProdRefInNonRoot), }}, } for _, tt := range tests { diff --git a/read_test.go b/read_test.go index 444d611..b000f3c 100644 --- a/read_test.go +++ b/read_test.go @@ -46,9 +46,9 @@ func (qmExtension) NewAttrGroup(xml.Name) spec.AttrGroup { return &fakeAttr{} } -func (qmExtension) NewElementDecoder(parent interface{}, _ string) spec.ElementDecoder { - if e, ok := parent.(*Resources); ok { - return &fakeAssetDecoder{resources: e} +func (qmExtension) NewElementDecoder(name xml.Name) spec.GetterElementDecoder { + if name.Space == fakeSpec.Namespace && name.Local == "fakeasset" { + return new(fakeAssetDecoder) } return nil } @@ -77,7 +77,7 @@ func (f *fakeAsset) Identify() uint32 { // XMLName returns the xml identifier of the resource. func (fakeAsset) XMLName() xml.Name { - return xml.Name{Space: Namespace, Local: "fake"} + return xml.Name{Space: Namespace, Local: "fakeasset"} } type fakeAttr struct { @@ -100,12 +100,16 @@ func (f *fakeAttr) Unmarshal3MFAttr(a spec.XMLAttr) error { type fakeAssetDecoder struct { baseDecoder - resources *Resources + fa fakeAsset +} + +func (f *fakeAssetDecoder) Element() interface{} { + return &f.fa } func (f *fakeAssetDecoder) Start(att []spec.XMLAttr) error { id, _ := strconv.ParseUint(string(att[0].Value), 10, 32) - f.resources.Assets = append(f.resources.Assets, &fakeAsset{ID: uint32(id)}) + f.fa.ID = uint32(id) return nil } diff --git a/slices/decoder.go b/slices/decoder.go index e07ba2b..4dd286a 100644 --- a/slices/decoder.go +++ b/slices/decoder.go @@ -12,9 +12,9 @@ import ( "github.com/hpinc/go3mf/spec" ) -func (Spec) NewElementDecoder(parent interface{}, name string) spec.ElementDecoder { - if name == attrSliceStack { - return &sliceStackDecoder{resources: parent.(*go3mf.Resources)} +func (Spec) NewElementDecoder(name xml.Name) spec.GetterElementDecoder { + if name.Space == Namespace && name.Local == attrSliceStack { + return new(sliceStackDecoder) } return nil } @@ -49,12 +49,11 @@ func (u *ObjectAttr) Unmarshal3MFAttr(a spec.XMLAttr) error { type sliceStackDecoder struct { baseDecoder - resources *go3mf.Resources - resource SliceStack + resource SliceStack } -func (d *sliceStackDecoder) End() { - d.resources.Assets = append(d.resources.Assets, &d.resource) +func (d *sliceStackDecoder) Element() interface{} { + return &d.resource } func (d *sliceStackDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { diff --git a/slices/decoder_test.go b/slices/decoder_test.go index 10b50ad..9c7e0eb 100644 --- a/slices/decoder_test.go +++ b/slices/decoder_test.go @@ -95,16 +95,16 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("Resources@SliceStack#0: %v", specerr.NewParseAttrError("id", true)), - fmt.Sprintf("Resources@SliceStack#0: %v", specerr.NewParseAttrError("zbottom", false)), - fmt.Sprintf("Resources@SliceStack#0@Slice#0@Vertices@Point2D#0: %v", specerr.NewParseAttrError("x", true)), - fmt.Sprintf("Resources@SliceStack#0@Slice#0@Vertices@Point2D#1: %v", specerr.NewParseAttrError("y", true)), - fmt.Sprintf("Resources@SliceStack#0@Slice#1: %v", specerr.NewParseAttrError("ztop", true)), - fmt.Sprintf("Resources@SliceStack#0@Slice#1@Polygon#0: %v", specerr.NewParseAttrError("startv", true)), - fmt.Sprintf("Resources@SliceStack#0@Slice#1@Polygon#0@Segment#1: %v", specerr.NewParseAttrError("v2", true)), - fmt.Sprintf("Resources@SliceStack#0@SliceRef#0: %v", specerr.NewParseAttrError("slicestackid", true)), - fmt.Sprintf("Resources@Object#0: %v", specerr.NewParseAttrError("meshresolution", false)), - fmt.Sprintf("Resources@Object#0: %v", specerr.NewParseAttrError("slicestackid", true)), + fmt.Sprintf("model@resources@slicestack#0: %v", specerr.NewParseAttrError("id", true)), + fmt.Sprintf("model@resources@slicestack#0: %v", specerr.NewParseAttrError("zbottom", false)), + fmt.Sprintf("model@resources@slicestack#0@slice#0@vertices@vertex#0: %v", specerr.NewParseAttrError("x", true)), + fmt.Sprintf("model@resources@slicestack#0@slice#0@vertices@vertex#1: %v", specerr.NewParseAttrError("y", true)), + fmt.Sprintf("model@resources@slicestack#0@slice#1: %v", specerr.NewParseAttrError("ztop", true)), + fmt.Sprintf("model@resources@slicestack#0@slice#1@polygon#0: %v", specerr.NewParseAttrError("startv", true)), + fmt.Sprintf("model@resources@slicestack#0@slice#1@polygon#0@segment#1: %v", specerr.NewParseAttrError("v2", true)), + fmt.Sprintf("model@resources@slicestack#0@sliceref#0: %v", specerr.NewParseAttrError("slicestackid", true)), + fmt.Sprintf("model@resources@object#0: %v", specerr.NewParseAttrError("meshresolution", false)), + fmt.Sprintf("model@resources@object#0: %v", specerr.NewParseAttrError("slicestackid", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/slices/validate_test.go b/slices/validate_test.go index 14627df..26da3ea 100644 --- a/slices/validate_test.go +++ b/slices/validate_test.go @@ -33,7 +33,7 @@ func TestValidate(t *testing.T) { }}}, }}, }, []string{ - fmt.Sprintf("Resources@Object#0: %v", errors.ErrInvalidObject), + fmt.Sprintf("resources@object#0: %v", errors.ErrInvalidObject), }}, {"child", &go3mf.Model{Childs: map[string]*go3mf.ChildModel{ "/other.model": {Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -43,8 +43,8 @@ func TestValidate(t *testing.T) { &SliceStack{ID: 2}, }}}, }}, []string{ - fmt.Sprintf("/other.model@Resources@SliceStack#0: %v", ErrSlicesAndRefs), - fmt.Sprintf("/that.model@Resources@SliceStack#0: %v", ErrSlicesAndRefs), + fmt.Sprintf("/other.model@resources@slicestack#0: %v", ErrSlicesAndRefs), + fmt.Sprintf("/that.model@resources@slicestack#0: %v", ErrSlicesAndRefs), }}, {"slicestack", &go3mf.Model{Resources: go3mf.Resources{ Assets: []go3mf.Asset{&SliceStack{ @@ -60,13 +60,13 @@ func TestValidate(t *testing.T) { }, }}, }}, []string{ - fmt.Sprintf("Resources@SliceStack#0@Slice#0: %v", &errors.MissingFieldError{Name: attrZTop}), - fmt.Sprintf("Resources@SliceStack#0@Slice#1: %v", ErrSliceSmallTopZ), - fmt.Sprintf("Resources@SliceStack#0@Slice#1: %v", ErrSliceInsufficientVertices), - fmt.Sprintf("Resources@SliceStack#0@Slice#1: %v", ErrSliceInsufficientPolygons), - fmt.Sprintf("Resources@SliceStack#0@Slice#2@Polygon#0: %v", ErrSliceInsufficientSegments), - fmt.Sprintf("Resources@SliceStack#0@Slice#3: %v", ErrSliceNoMonotonic), - fmt.Sprintf("Resources@SliceStack#0@Slice#4: %v", ErrSliceNoMonotonic), + fmt.Sprintf("resources@slicestack#0@slice#0: %v", &errors.MissingFieldError{Name: attrZTop}), + fmt.Sprintf("resources@slicestack#0@slice#1: %v", ErrSliceSmallTopZ), + fmt.Sprintf("resources@slicestack#0@slice#1: %v", ErrSliceInsufficientVertices), + fmt.Sprintf("resources@slicestack#0@slice#1: %v", ErrSliceInsufficientPolygons), + fmt.Sprintf("resources@slicestack#0@slice#2@polygon#0: %v", ErrSliceInsufficientSegments), + fmt.Sprintf("resources@slicestack#0@slice#3: %v", ErrSliceNoMonotonic), + fmt.Sprintf("resources@slicestack#0@slice#4: %v", ErrSliceNoMonotonic), }}, {"sliceref", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{ @@ -91,13 +91,13 @@ func TestValidate(t *testing.T) { }, }}, }}, []string{ - fmt.Sprintf("Resources@SliceStack#1@SliceRef#0: %v", &errors.MissingFieldError{Name: attrSlicePath}), - fmt.Sprintf("Resources@SliceStack#1@SliceRef#0: %v", &errors.MissingFieldError{Name: attrSliceRefID}), - fmt.Sprintf("Resources@SliceStack#1@SliceRef#1: %v", ErrSliceRefSamePart), - fmt.Sprintf("Resources@SliceStack#1@SliceRef#2: %v", errors.ErrMissingResource), - fmt.Sprintf("Resources@SliceStack#1@SliceRef#3: %v", ErrSliceRefRef), - fmt.Sprintf("Resources@SliceStack#1@SliceRef#4: %v", ErrNonSliceStack), - fmt.Sprintf("Resources@SliceStack#1@SliceRef#6: %v", ErrSliceNoMonotonic), + fmt.Sprintf("resources@slicestack#1@sliceref#0: %v", &errors.MissingFieldError{Name: attrSlicePath}), + fmt.Sprintf("resources@slicestack#1@sliceref#0: %v", &errors.MissingFieldError{Name: attrSliceRefID}), + fmt.Sprintf("resources@slicestack#1@sliceref#1: %v", ErrSliceRefSamePart), + fmt.Sprintf("resources@slicestack#1@sliceref#2: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@slicestack#1@sliceref#3: %v", ErrSliceRefRef), + fmt.Sprintf("resources@slicestack#1@sliceref#4: %v", ErrNonSliceStack), + fmt.Sprintf("resources@slicestack#1@sliceref#6: %v", ErrSliceNoMonotonic), }}, {"info", &go3mf.Model{Build: go3mf.Build{Items: []*go3mf.Item{ {ObjectID: 7}, @@ -153,18 +153,18 @@ func TestValidate(t *testing.T) { SliceStackID: 11, }}}, }}}, []string{ - fmt.Sprintf("Resources@Object#0@Mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("Resources@Object#0@Mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("Resources@Object#0: %v", errors.ErrMissingResource), - fmt.Sprintf("Resources@Object#1: %v", ErrSliceInvalidTranform), - fmt.Sprintf("Resources@Object#1: %v", ErrSliceExtRequired), - fmt.Sprintf("Resources@Object#2: %v", &errors.MissingFieldError{Name: attrSliceRefID}), - fmt.Sprintf("Resources@Object#3: %v", ErrNonSliceStack), - fmt.Sprintf("Resources@Object#4: %v", ErrSliceInvalidTranform), - fmt.Sprintf("Resources@Object#4: %v", ErrSlicePolygonNotClosed), - fmt.Sprintf("Resources@Object#5: %v", ErrSliceInvalidTranform), - fmt.Sprintf("Resources@Object#5: %v", ErrSlicePolygonNotClosed), - fmt.Sprintf("Resources@Object#6@Components@Component#1: %v", errors.ErrRecursion), + fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("resources@object#0: %v", errors.ErrMissingResource), + fmt.Sprintf("resources@object#1: %v", ErrSliceInvalidTranform), + fmt.Sprintf("resources@object#1: %v", ErrSliceExtRequired), + fmt.Sprintf("resources@object#2: %v", &errors.MissingFieldError{Name: attrSliceRefID}), + fmt.Sprintf("resources@object#3: %v", ErrNonSliceStack), + fmt.Sprintf("resources@object#4: %v", ErrSliceInvalidTranform), + fmt.Sprintf("resources@object#4: %v", ErrSlicePolygonNotClosed), + fmt.Sprintf("resources@object#5: %v", ErrSliceInvalidTranform), + fmt.Sprintf("resources@object#5: %v", ErrSlicePolygonNotClosed), + fmt.Sprintf("resources@object#6@components@component#1: %v", errors.ErrRecursion), }}, } for _, tt := range tests { diff --git a/spec/encoding.go b/spec/encoding.go index c98ea98..72be816 100644 --- a/spec/encoding.go +++ b/spec/encoding.go @@ -17,6 +17,11 @@ type UnmarshalerAttr interface { Unmarshal3MFAttr(XMLAttr) error } +type GetterElementDecoder interface { + ElementDecoder + Element() interface{} +} + // ElementDecoder defines the minimum contract to decode a 3MF node. type ElementDecoder interface { Start([]XMLAttr) error diff --git a/spec/spec.go b/spec/spec.go index 6d319c9..f52fef6 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -45,7 +45,7 @@ func LoadValidator(ns string) (ValidateSpec, bool) { // Specs may implement ValidateSpec. type Spec interface { NewAttrGroup(parent xml.Name) AttrGroup - NewElementDecoder(parent interface{}, name string) ElementDecoder + NewElementDecoder(name xml.Name) GetterElementDecoder } // If a Spec implemented ValidateSpec, then model.Validate will call From aab38bd501e75b78061da6e12654cf0ac2a467dc Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 13 Oct 2021 12:26:39 +0200 Subject: [PATCH 20/23] simplify unknown any decoder --- decoder.go | 14 ++++---------- encoder_test.go | 12 ++++++------ read_test.go | 16 ++++++++-------- spec/spec.go | 7 +++++++ spec/unknown.go | 43 +++++++++++++++---------------------------- 5 files changed, 40 insertions(+), 52 deletions(-) diff --git a/decoder.go b/decoder.go index cf5aca1..7e37521 100644 --- a/decoder.go +++ b/decoder.go @@ -39,16 +39,13 @@ func (d *modelDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { i = len(d.model.Metadata) } } - } else if ext, ok := spec.Load(name.Space); ok { - dec := ext.NewElementDecoder(name) + } else { + dec := spec.NewElementDecoder(name) child = dec if dec != nil { d.model.Any = append(d.model.Any, dec.Element().(spec.Marshaler)) } i = -1 - } else { - child = spec.NewAnyUnknownDecoder(name, &d.model.Any) - i = -1 } return } @@ -420,16 +417,13 @@ func (d *meshDecoder) Child(name xml.Name) (i int, child spec.ElementDecoder) { child = &trianglesDecoder{resource: d.resource} i = -1 } - } else if ext, ok := spec.Load(name.Space); ok { - dec := ext.NewElementDecoder(name) + } else { + dec := spec.NewElementDecoder(name) child = dec if dec != nil { d.resource.Mesh.Any = append(d.resource.Mesh.Any, dec.Element().(spec.Marshaler)) } i = -1 - } else { - child = spec.NewAnyUnknownDecoder(name, &d.resource.Mesh.Any) - i = -1 } return } diff --git a/encoder_test.go b/encoder_test.go index e621b23..98a2510 100644 --- a/encoder_test.go +++ b/encoder_test.go @@ -46,18 +46,18 @@ func TestMarshalModel(t *testing.T) { Units: UnitMillimeter, Language: "en-US", Path: "/3D/3dmodel.model", Thumbnail: "/thumbnail.png", Extensions: []Extension{fakeSpec, fooSpec}, AnyAttr: spec.AnyAttr{&fakeAttr{Value: "model_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo1"}}}}, - Any: spec.Any{spec.UnknownTokens{ + Any: spec.Any{&spec.UnknownTokens{Token: []xml.Token{ xml.StartElement{Name: fooName}, xml.EndElement{Name: fooName}, - }}, + }}}, Resources: Resources{ Assets: []Asset{ - &UnknownAsset{UnknownTokens: spec.UnknownTokens{ + &UnknownAsset{UnknownTokens: spec.UnknownTokens{Token: []xml.Token{ xml.StartElement{Name: fooName, Attr: []xml.Attr{{Name: xml.Name{Local: "n1"}, Value: "v1"}}}, xml.StartElement{Name: xml.Name{Space: fooName.Space, Local: "child"}}, xml.EndElement{Name: xml.Name{Space: fooName.Space, Local: "child"}}, xml.EndElement{Name: fooName}, - }}, + }}}, &BaseMaterials{ID: 5, Materials: []Base{ {Name: "Blue PLA", Color: color.RGBA{0, 0, 255, 255}, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo6"}}}}}, {Name: "Red ABS", Color: color.RGBA{255, 0, 0, 255}}, @@ -68,10 +68,10 @@ func TestMarshalModel(t *testing.T) { ID: 8, Name: "Box 1", PartNumber: "11111111-1111-1111-1111-111111111111", Thumbnail: "/a.png", AnyAttr: spec.AnyAttr{&fakeAttr{Value: "object_fake"}, &spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "foo3"}}}}, PID: 1, PIndex: 1, Type: ObjectTypeModel, Mesh: &Mesh{ - Any: spec.Any{spec.UnknownTokens{ + Any: spec.Any{&spec.UnknownTokens{Token: []xml.Token{ xml.StartElement{Name: fooName}, xml.EndElement{Name: fooName}, - }}, + }}}, Vertices: Vertices{Vertex: []Point3D{ {0, 0, 0}, {100, 0, 0}, {100, 100, 0}, {0, 100, 0}, {0, 0, 100}, {100, 0, 100}, diff --git a/read_test.go b/read_test.go index b000f3c..6d516bf 100644 --- a/read_test.go +++ b/read_test.go @@ -335,10 +335,10 @@ func TestDecoder_processRootModel(t *testing.T) { ID: 8, Name: "Box 1", Thumbnail: "/a.png", PID: 5, PartNumber: "11111111-1111-1111-1111-111111111111", Mesh: &Mesh{ AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval9"}}}}, - Any: spec.Any{spec.UnknownTokens{ + Any: spec.Any{&spec.UnknownTokens{Token: []xml.Token{ xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "fake"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "fake"}}, - }}, + }}}, Vertices: Vertices{Vertex: []Point3D{ {0, 0, 0}, {100, 0, 0}, @@ -390,7 +390,7 @@ func TestDecoder_processRootModel(t *testing.T) { Resources: Resources{ Assets: []Asset{baseMaterials, &UnknownAsset{ id: 50, - UnknownTokens: spec.UnknownTokens{ + UnknownTokens: spec.UnknownTokens{Token: []xml.Token{ xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "resources"}, Attr: []xml.Attr{ {Name: xml.Name{Space: "", Local: "id"}, Value: "50"}, {Name: xml.Name{Space: "", Local: "name"}, Value: "test"}, @@ -404,7 +404,7 @@ func TestDecoder_processRootModel(t *testing.T) { xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "subresource"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "resource"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "resources"}}, - }, + }}, }}, Objects: []*Object{meshRes, components}, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval3"}}}}, }, @@ -413,18 +413,18 @@ func TestDecoder_processRootModel(t *testing.T) { }, AnyAttr: spec.AnyAttr{&spec.UnknownAttrs{Space: fooSpace, Attr: []xml.Attr{{Name: fooName, Value: "fooval"}}}}, Any: spec.Any{ - spec.UnknownTokens{ + &spec.UnknownTokens{Token: []xml.Token{ xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "other"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "other"}}, - }, - spec.UnknownTokens{ + }}, + &spec.UnknownTokens{Token: []xml.Token{ xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "other1"}, Attr: []xml.Attr{ {Name: xml.Name{Space: "", Local: "a"}, Value: "2"}, }}, xml.StartElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "child1"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "child1"}}, xml.EndElement{Name: xml.Name{Space: fooSpec.Namespace, Local: "other1"}}, - }, + }}, }, } want.Build.Items = append(want.Build.Items, &Item{ diff --git a/spec/spec.go b/spec/spec.go index f52fef6..9faae81 100644 --- a/spec/spec.go +++ b/spec/spec.go @@ -111,6 +111,13 @@ func NewAttrGroup(namespace string, parent xml.Name) AttrGroup { } } +func NewElementDecoder(name xml.Name) GetterElementDecoder { + if ext, ok := Load(name.Space); ok { + return ext.NewElementDecoder(name) + } + return &UnknownTokensDecoder{XMLName: name} +} + // Any is an extension point containing information. type Any []Marshaler diff --git a/spec/unknown.go b/spec/unknown.go index 430c78b..5e6a107 100644 --- a/spec/unknown.go +++ b/spec/unknown.go @@ -28,19 +28,21 @@ func (u *UnknownAttrs) Unmarshal3MFAttr(a XMLAttr) error { // UnknownTokens represents a section of an xml // that cannot be decoded by any loaded Spec. -type UnknownTokens []xml.Token +type UnknownTokens struct { + Token []xml.Token +} // XMLName returns the xml identifier of the resource. func (u UnknownTokens) XMLName() xml.Name { - if len(u) == 0 { + if len(u.Token) == 0 { return xml.Name{} } - start, _ := u[0].(xml.StartElement) + start, _ := u.Token[0].(xml.StartElement) return start.Name } func (u UnknownTokens) Marshal3MF(enc Encoder, _ *xml.StartElement) error { - for _, t := range u { + for _, t := range u.Token { enc.EncodeToken(t) } return nil @@ -49,17 +51,21 @@ func (u UnknownTokens) Marshal3MF(enc Encoder, _ *xml.StartElement) error { // UnknownTokensDecoder can be used by spec decoders to maintain the // xml tree elements of unknown extensions. type UnknownTokensDecoder struct { - Name xml.Name + XMLName xml.Name tokens UnknownTokens } func NewUnknownDecoder(name xml.Name) *UnknownTokensDecoder { return &UnknownTokensDecoder{ - Name: name, + XMLName: name, } } +func (d *UnknownTokensDecoder) Element() interface{} { + return &d.tokens +} + func (d *UnknownTokensDecoder) Start(attrs []XMLAttr) error { var xattrs []xml.Attr if len(attrs) > 0 { @@ -69,39 +75,20 @@ func (d *UnknownTokensDecoder) Start(attrs []XMLAttr) error { } } d.AppendToken(xml.StartElement{ - Name: d.Name, + Name: d.XMLName, Attr: xattrs, }) return nil } func (d *UnknownTokensDecoder) End() { - d.AppendToken(xml.EndElement{Name: d.Name}) + d.AppendToken(xml.EndElement{Name: d.XMLName}) } func (d *UnknownTokensDecoder) AppendToken(t xml.Token) { - d.tokens = append(d.tokens, t) + d.tokens.Token = append(d.tokens.Token, t) } func (d UnknownTokensDecoder) Tokens() UnknownTokens { return d.tokens } - -type AnyUnknownDecoder struct { - UnknownTokensDecoder - Any *Any -} - -func NewAnyUnknownDecoder(name xml.Name, any *Any) *AnyUnknownDecoder { - return &AnyUnknownDecoder{ - UnknownTokensDecoder: UnknownTokensDecoder{ - Name: name, - }, - Any: any, - } -} - -func (d *AnyUnknownDecoder) End() { - d.UnknownTokensDecoder.End() - *d.Any = append(*d.Any, d.Tokens()) -} From 16880713a760974b219615ef1af600101e65451a Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 13 Oct 2021 13:13:07 +0200 Subject: [PATCH 21/23] add model context to validation --- beamlattice/validate_test.go | 36 ++++++------ materials/validate_test.go | 56 +++++++++--------- production/validate_test.go | 34 +++++------ slices/validate_test.go | 58 +++++++++---------- validate.go | 10 +++- validate_test.go | 108 +++++++++++++++++------------------ 6 files changed, 154 insertions(+), 148 deletions(-) diff --git a/beamlattice/validate_test.go b/beamlattice/validate_test.go index 46d2ec4..eecd791 100644 --- a/beamlattice/validate_test.go +++ b/beamlattice/validate_test.go @@ -24,21 +24,21 @@ func TestValidate(t *testing.T) { {ID: 1, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{}}}}, }}}, }}, []string{ - fmt.Sprintf("/other.model@resources@object#0@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("/other.model@resources@object#0@mesh@beamlattice: %v", &errors.MissingFieldError{Name: attrMinLength}), - fmt.Sprintf("/other.model@resources@object#0@mesh@beamlattice: %v", &errors.MissingFieldError{Name: attrRadius}), - fmt.Sprintf("/other.model@resources@object#0@mesh@beamlattice: %v", ErrLatticeClippedNoMesh), + fmt.Sprintf("/other.model@model@resources@object#0@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("/other.model@model@resources@object#0@mesh@beamlattice: %v", &errors.MissingFieldError{Name: attrMinLength}), + fmt.Sprintf("/other.model@model@resources@object#0@mesh@beamlattice: %v", &errors.MissingFieldError{Name: attrRadius}), + fmt.Sprintf("/other.model@model@resources@object#0@mesh@beamlattice: %v", ErrLatticeClippedNoMesh), }}, {"object without beamlattice", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Mesh: &go3mf.Mesh{}}, }}}, []string{ - fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrInsufficientTriangles), }}, {"object with components", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 2}}}}, }}}, []string{ - fmt.Sprintf("resources@object#0@components@component#0: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@object#0@components@component#0: %v", errors.ErrMissingResource), }}, {"object incorret type", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Type: go3mf.ObjectTypeOther, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{ @@ -51,9 +51,9 @@ func TestValidate(t *testing.T) { MinLength: 1, Radius: 1, ClipMode: ClipInside, }}}}, }}}, []string{ - fmt.Sprintf("resources@object#0@mesh@beamlattice: %v", ErrLatticeObjType), - fmt.Sprintf("resources@object#1@mesh@beamlattice: %v", ErrLatticeObjType), - fmt.Sprintf("resources@object#2@mesh@beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("model@resources@object#1@mesh@beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("model@resources@object#2@mesh@beamlattice: %v", ErrLatticeObjType), }}, {"incorrect mesh references", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{nil}}}, @@ -64,9 +64,9 @@ func TestValidate(t *testing.T) { MinLength: 1, Radius: 1, ClippingMeshID: 1, RepresentationMeshID: 2, }}}}, }}}, []string{ - fmt.Sprintf("resources@object#1@mesh@beamlattice: %v", errors.ErrMissingResource), - fmt.Sprintf("resources@object#1@mesh@beamlattice: %v", errors.ErrRecursion), - fmt.Sprintf("resources@object#2@mesh@beamlattice: %v", ErrLatticeInvalidMesh), + fmt.Sprintf("model@resources@object#1@mesh@beamlattice: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@object#1@mesh@beamlattice: %v", errors.ErrRecursion), + fmt.Sprintf("model@resources@object#2@mesh@beamlattice: %v", ErrLatticeInvalidMesh), }}, {"incorrect beams", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ @@ -75,10 +75,10 @@ func TestValidate(t *testing.T) { }, }}}}}, }}}, []string{ - fmt.Sprintf("resources@object#0@mesh@beamlattice@beam#0: %v", ErrLatticeSameVertex), - fmt.Sprintf("resources@object#0@mesh@beamlattice@beam#1: %v", ErrLatticeSameVertex), - fmt.Sprintf("resources@object#0@mesh@beamlattice@beam#1: %v", ErrLatticeBeamR2), - fmt.Sprintf("resources@object#0@mesh@beamlattice@beam#2: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beam#0: %v", ErrLatticeSameVertex), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beam#1: %v", ErrLatticeSameVertex), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beam#1: %v", ErrLatticeBeamR2), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beam#2: %v", errors.ErrIndexOutOfBounds), }}, {"incorrect beamseat", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ @@ -87,7 +87,7 @@ func TestValidate(t *testing.T) { }}, BeamSets: BeamSets{BeamSet: []BeamSet{{Refs: []uint32{0, 2, 3}}}}, }}}}, }}}, []string{ - fmt.Sprintf("resources@object#0@mesh@beamlattice@beamset#0: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beamset#0: %v", errors.ErrIndexOutOfBounds), }}, } for _, tt := range tests { diff --git a/materials/validate_test.go b/materials/validate_test.go index 6b68c9d..42ddbb1 100644 --- a/materials/validate_test.go +++ b/materials/validate_test.go @@ -27,10 +27,10 @@ func TestValidate(t *testing.T) { &MultiProperties{ID: 2}, }}}, }}, []string{ - fmt.Sprintf("/other.model@resources@colorgroup#0: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("/that.model@resources@multiproperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), - fmt.Sprintf("/that.model@resources@multiproperties#0: %v", ErrMultiBlend), - fmt.Sprintf("/that.model@resources@multiproperties#0: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("/other.model@model@resources@colorgroup#0: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("/that.model@model@resources@multiproperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), + fmt.Sprintf("/that.model@model@resources@multiproperties#0: %v", ErrMultiBlend), + fmt.Sprintf("/that.model@model@resources@multiproperties#0: %v", errors.ErrEmptyResourceProps), }}, {"multi", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -48,15 +48,15 @@ func TestValidate(t *testing.T) { &MultiProperties{ID: 9, Multis: []Multi{{PIndices: []uint32{}}}, PIDs: []uint32{1, 3}}, }}, }, []string{ - fmt.Sprintf("resources@multiproperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), - fmt.Sprintf("resources@multiproperties#0: %v", ErrMultiBlend), - fmt.Sprintf("resources@multiproperties#0: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("resources@multiproperties#1: %v", ErrMultiRefMulti), - fmt.Sprintf("resources@multiproperties#1: %v", errors.ErrMissingResource), - fmt.Sprintf("resources@multiproperties#6@multi#0: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("resources@multiproperties#7: %v", ErrMaterialMulti), - fmt.Sprintf("resources@multiproperties#7: %v", ErrMultiColors), - fmt.Sprintf("resources@multiproperties#8: %v", ErrMaterialMulti), + fmt.Sprintf("model@resources@multiproperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), + fmt.Sprintf("model@resources@multiproperties#0: %v", ErrMultiBlend), + fmt.Sprintf("model@resources@multiproperties#0: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model@resources@multiproperties#1: %v", ErrMultiRefMulti), + fmt.Sprintf("model@resources@multiproperties#1: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@multiproperties#6@multi#0: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model@resources@multiproperties#7: %v", ErrMaterialMulti), + fmt.Sprintf("model@resources@multiproperties#7: %v", ErrMultiColors), + fmt.Sprintf("model@resources@multiproperties#8: %v", ErrMaterialMulti), }}, {"missingTextPart", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -64,9 +64,9 @@ func TestValidate(t *testing.T) { &Texture2D{ID: 2, ContentType: TextureTypePNG, Path: "/a.png"}, }}, }, []string{ - fmt.Sprintf("resources@texture2d#0: %v", &errors.MissingFieldError{Name: attrPath}), - fmt.Sprintf("resources@texture2d#0: %v", &errors.MissingFieldError{Name: attrContentType}), - fmt.Sprintf("resources@texture2d#1: %v", ErrMissingTexturePart), + fmt.Sprintf("model@resources@texture2d#0: %v", &errors.MissingFieldError{Name: attrPath}), + fmt.Sprintf("model@resources@texture2d#0: %v", &errors.MissingFieldError{Name: attrContentType}), + fmt.Sprintf("model@resources@texture2d#1: %v", ErrMissingTexturePart), }}, {"textureGroup", &go3mf.Model{ Attachments: []go3mf.Attachment{{Path: "/a.png"}}, @@ -78,10 +78,10 @@ func TestValidate(t *testing.T) { &Texture2DGroup{ID: 5, TextureID: 100, Coords: []TextureCoord{{}}}, }}, }, []string{ - fmt.Sprintf("resources@texture2dgroup#1: %v", &errors.MissingFieldError{Name: attrTexID}), - fmt.Sprintf("resources@texture2dgroup#1: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("resources@texture2dgroup#3: %v", ErrTextureReference), - fmt.Sprintf("resources@texture2dgroup#4: %v", ErrTextureReference), + fmt.Sprintf("model@resources@texture2dgroup#1: %v", &errors.MissingFieldError{Name: attrTexID}), + fmt.Sprintf("model@resources@texture2dgroup#1: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model@resources@texture2dgroup#3: %v", ErrTextureReference), + fmt.Sprintf("model@resources@texture2dgroup#4: %v", ErrTextureReference), }}, {"colorGroup", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -90,8 +90,8 @@ func TestValidate(t *testing.T) { &ColorGroup{ID: 3, Colors: []color.RGBA{{R: 1}, {}}}, }}, }, []string{ - fmt.Sprintf("resources@colorgroup#0: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("resources@colorgroup#2@color#1: %v", &errors.MissingFieldError{Name: attrColor}), + fmt.Sprintf("model@resources@colorgroup#0: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model@resources@colorgroup#2@color#1: %v", &errors.MissingFieldError{Name: attrColor}), }}, {"composite", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -105,12 +105,12 @@ func TestValidate(t *testing.T) { &CompositeMaterials{ID: 5, MaterialID: 2, Indices: []uint32{0, 1}, Composites: []Composite{{Values: []float32{1, 2}}}}, &CompositeMaterials{ID: 6, MaterialID: 100, Indices: []uint32{0, 1}, Composites: []Composite{{Values: []float32{1, 2}}}}, }}}, []string{ - fmt.Sprintf("resources@compositematerials#1: %v", &errors.MissingFieldError{Name: attrMatID}), - fmt.Sprintf("resources@compositematerials#1: %v", &errors.MissingFieldError{Name: attrMatIndices}), - fmt.Sprintf("resources@compositematerials#1: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("resources@compositematerials#3: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("resources@compositematerials#4: %v", ErrCompositeBase), - fmt.Sprintf("resources@compositematerials#5: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@compositematerials#1: %v", &errors.MissingFieldError{Name: attrMatID}), + fmt.Sprintf("model@resources@compositematerials#1: %v", &errors.MissingFieldError{Name: attrMatIndices}), + fmt.Sprintf("model@resources@compositematerials#1: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model@resources@compositematerials#3: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model@resources@compositematerials#4: %v", ErrCompositeBase), + fmt.Sprintf("model@resources@compositematerials#5: %v", errors.ErrMissingResource), }}, } for _, tt := range tests { diff --git a/production/validate_test.go b/production/validate_test.go index 5047922..3323a5d 100644 --- a/production/validate_test.go +++ b/production/validate_test.go @@ -23,15 +23,15 @@ func TestValidate(t *testing.T) { want []string }{ {"buildNoUUID", &go3mf.Model{Build: go3mf.Build{}}, []string{ - fmt.Sprintf("build: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model@build: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"buildEmptyUUID", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{}}}}, []string{ - fmt.Sprintf("build: %v", ErrUUID), + fmt.Sprintf("model@build: %v", ErrUUID), }}, {"buildNonValidUUID", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{"a-b-c-d"}}}}, []string{ - fmt.Sprintf("build: %v", ErrUUID), + fmt.Sprintf("model@build: %v", ErrUUID), }}, {"extReq", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, @@ -43,7 +43,7 @@ func TestValidate(t *testing.T) { AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ {ObjectID: 1, AnyAttr: spec.AnyAttr{&ItemAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d478", Path: "/other.model"}}}, }}}, []string{ - fmt.Sprintf("/other.model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model@model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"items", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ @@ -54,11 +54,11 @@ func TestValidate(t *testing.T) { }}, Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, Resources: go3mf.Resources{Objects: []*go3mf.Object{{ID: 1, Mesh: validMesh.Mesh}}}}, []string{ - fmt.Sprintf("build@item#1: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("build@item#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("build@item#3: %v", ErrUUID), - fmt.Sprintf("/other.model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model@build@item#1: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model@build@item#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model@build@item#3: %v", ErrUUID), + fmt.Sprintf("/other.model@model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"components", &go3mf.Model{Resources: go3mf.Resources{ Objects: []*go3mf.Object{ @@ -70,10 +70,10 @@ func TestValidate(t *testing.T) { }}}, }, }, Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}}, []string{ - fmt.Sprintf("resources@object#0: %v", ErrUUID), - fmt.Sprintf("resources@object#1@components@component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("resources@object#1@components@component#1: %v", ErrUUID), - fmt.Sprintf("resources@object#1@components@component#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model@resources@object#0: %v", ErrUUID), + fmt.Sprintf("model@resources@object#1@components@component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model@resources@object#1@components@component#1: %v", ErrUUID), + fmt.Sprintf("model@resources@object#1@components@component#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"child", &go3mf.Model{Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}, Childs: map[string]*go3mf.ChildModel{ @@ -83,10 +83,10 @@ func TestValidate(t *testing.T) { {ObjectID: 1, AnyAttr: spec.AnyAttr{&ComponentAttr{Path: "/b.model"}}}, }}}, }}}}}, []string{ - fmt.Sprintf("/b.model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model@resources@object#0@components@component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model@resources@object#0@components@component#0: %v", ErrProdRefInNonRoot), + fmt.Sprintf("/b.model@model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model@model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model@model@resources@object#0@components@component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model@model@resources@object#0@components@component#0: %v", ErrProdRefInNonRoot), }}, } for _, tt := range tests { diff --git a/slices/validate_test.go b/slices/validate_test.go index 26da3ea..8714c5f 100644 --- a/slices/validate_test.go +++ b/slices/validate_test.go @@ -33,7 +33,7 @@ func TestValidate(t *testing.T) { }}}, }}, }, []string{ - fmt.Sprintf("resources@object#0: %v", errors.ErrInvalidObject), + fmt.Sprintf("model@resources@object#0: %v", errors.ErrInvalidObject), }}, {"child", &go3mf.Model{Childs: map[string]*go3mf.ChildModel{ "/other.model": {Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -43,8 +43,8 @@ func TestValidate(t *testing.T) { &SliceStack{ID: 2}, }}}, }}, []string{ - fmt.Sprintf("/other.model@resources@slicestack#0: %v", ErrSlicesAndRefs), - fmt.Sprintf("/that.model@resources@slicestack#0: %v", ErrSlicesAndRefs), + fmt.Sprintf("/other.model@model@resources@slicestack#0: %v", ErrSlicesAndRefs), + fmt.Sprintf("/that.model@model@resources@slicestack#0: %v", ErrSlicesAndRefs), }}, {"slicestack", &go3mf.Model{Resources: go3mf.Resources{ Assets: []go3mf.Asset{&SliceStack{ @@ -60,13 +60,13 @@ func TestValidate(t *testing.T) { }, }}, }}, []string{ - fmt.Sprintf("resources@slicestack#0@slice#0: %v", &errors.MissingFieldError{Name: attrZTop}), - fmt.Sprintf("resources@slicestack#0@slice#1: %v", ErrSliceSmallTopZ), - fmt.Sprintf("resources@slicestack#0@slice#1: %v", ErrSliceInsufficientVertices), - fmt.Sprintf("resources@slicestack#0@slice#1: %v", ErrSliceInsufficientPolygons), - fmt.Sprintf("resources@slicestack#0@slice#2@polygon#0: %v", ErrSliceInsufficientSegments), - fmt.Sprintf("resources@slicestack#0@slice#3: %v", ErrSliceNoMonotonic), - fmt.Sprintf("resources@slicestack#0@slice#4: %v", ErrSliceNoMonotonic), + fmt.Sprintf("model@resources@slicestack#0@slice#0: %v", &errors.MissingFieldError{Name: attrZTop}), + fmt.Sprintf("model@resources@slicestack#0@slice#1: %v", ErrSliceSmallTopZ), + fmt.Sprintf("model@resources@slicestack#0@slice#1: %v", ErrSliceInsufficientVertices), + fmt.Sprintf("model@resources@slicestack#0@slice#1: %v", ErrSliceInsufficientPolygons), + fmt.Sprintf("model@resources@slicestack#0@slice#2@polygon#0: %v", ErrSliceInsufficientSegments), + fmt.Sprintf("model@resources@slicestack#0@slice#3: %v", ErrSliceNoMonotonic), + fmt.Sprintf("model@resources@slicestack#0@slice#4: %v", ErrSliceNoMonotonic), }}, {"sliceref", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{ @@ -91,13 +91,13 @@ func TestValidate(t *testing.T) { }, }}, }}, []string{ - fmt.Sprintf("resources@slicestack#1@sliceref#0: %v", &errors.MissingFieldError{Name: attrSlicePath}), - fmt.Sprintf("resources@slicestack#1@sliceref#0: %v", &errors.MissingFieldError{Name: attrSliceRefID}), - fmt.Sprintf("resources@slicestack#1@sliceref#1: %v", ErrSliceRefSamePart), - fmt.Sprintf("resources@slicestack#1@sliceref#2: %v", errors.ErrMissingResource), - fmt.Sprintf("resources@slicestack#1@sliceref#3: %v", ErrSliceRefRef), - fmt.Sprintf("resources@slicestack#1@sliceref#4: %v", ErrNonSliceStack), - fmt.Sprintf("resources@slicestack#1@sliceref#6: %v", ErrSliceNoMonotonic), + fmt.Sprintf("model@resources@slicestack#1@sliceref#0: %v", &errors.MissingFieldError{Name: attrSlicePath}), + fmt.Sprintf("model@resources@slicestack#1@sliceref#0: %v", &errors.MissingFieldError{Name: attrSliceRefID}), + fmt.Sprintf("model@resources@slicestack#1@sliceref#1: %v", ErrSliceRefSamePart), + fmt.Sprintf("model@resources@slicestack#1@sliceref#2: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@slicestack#1@sliceref#3: %v", ErrSliceRefRef), + fmt.Sprintf("model@resources@slicestack#1@sliceref#4: %v", ErrNonSliceStack), + fmt.Sprintf("model@resources@slicestack#1@sliceref#6: %v", ErrSliceNoMonotonic), }}, {"info", &go3mf.Model{Build: go3mf.Build{Items: []*go3mf.Item{ {ObjectID: 7}, @@ -153,18 +153,18 @@ func TestValidate(t *testing.T) { SliceStackID: 11, }}}, }}}, []string{ - fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("resources@object#0: %v", errors.ErrMissingResource), - fmt.Sprintf("resources@object#1: %v", ErrSliceInvalidTranform), - fmt.Sprintf("resources@object#1: %v", ErrSliceExtRequired), - fmt.Sprintf("resources@object#2: %v", &errors.MissingFieldError{Name: attrSliceRefID}), - fmt.Sprintf("resources@object#3: %v", ErrNonSliceStack), - fmt.Sprintf("resources@object#4: %v", ErrSliceInvalidTranform), - fmt.Sprintf("resources@object#4: %v", ErrSlicePolygonNotClosed), - fmt.Sprintf("resources@object#5: %v", ErrSliceInvalidTranform), - fmt.Sprintf("resources@object#5: %v", ErrSlicePolygonNotClosed), - fmt.Sprintf("resources@object#6@components@component#1: %v", errors.ErrRecursion), + fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("model@resources@object#0: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@object#1: %v", ErrSliceInvalidTranform), + fmt.Sprintf("model@resources@object#1: %v", ErrSliceExtRequired), + fmt.Sprintf("model@resources@object#2: %v", &errors.MissingFieldError{Name: attrSliceRefID}), + fmt.Sprintf("model@resources@object#3: %v", ErrNonSliceStack), + fmt.Sprintf("model@resources@object#4: %v", ErrSliceInvalidTranform), + fmt.Sprintf("model@resources@object#4: %v", ErrSlicePolygonNotClosed), + fmt.Sprintf("model@resources@object#5: %v", ErrSliceInvalidTranform), + fmt.Sprintf("model@resources@object#5: %v", ErrSlicePolygonNotClosed), + fmt.Sprintf("model@resources@object#6@components@component#1: %v", errors.ErrRecursion), }}, } for _, tt := range tests { diff --git a/validate.go b/validate.go index 6e2b55c..1f7f111 100644 --- a/validate.go +++ b/validate.go @@ -66,7 +66,10 @@ func (m *Model) Validate() error { if err != nil { errs = errors.Append(errs, errors.Wrap(err, attrBuild)) } - return errs + if errs != nil { + return errors.Wrap(errs, attrModel) + } + return nil } func (item *Item) validate(m *Model) error { @@ -404,7 +407,10 @@ func (m *Model) ValidateCoherency() error { } } wg.Wait() - return errs + if errs != nil { + return errors.Wrap(errs, attrModel) + } + return nil } func isSolidObject(r *Object) bool { diff --git a/validate_test.go b/validate_test.go index ab59d70..9784700 100644 --- a/validate_test.go +++ b/validate_test.go @@ -27,26 +27,26 @@ func TestValidate(t *testing.T) { {}, {Path: "/.png"}, {Path: "/a.png"}, {Path: "a.png"}, {Path: "/b.png"}, {Path: "/a.png"}, {Path: "/a.png", Type: RelTypePrintTicket}, {Path: "/a.png", Type: RelTypePrintTicket}, }}, []string{ - fmt.Sprintf("/3D/3dmodel.model@relationship#0: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model@relationship#1: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model@relationship#3: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model@relationship#4: %v", errors.ErrOPCRelTarget), - fmt.Sprintf("/3D/3dmodel.model@relationship#5: %v", errors.ErrOPCDuplicatedRel), - fmt.Sprintf("/3D/3dmodel.model@relationship#6: %v", errors.ErrOPCContentType), - fmt.Sprintf("/3D/3dmodel.model@relationship#7: %v", errors.ErrOPCDuplicatedRel), - fmt.Sprintf("/3D/3dmodel.model@relationship#7: %v", errors.ErrOPCContentType), - fmt.Sprintf("/3D/3dmodel.model@relationship#7: %v", errors.ErrOPCDuplicatedTicket), + fmt.Sprintf("/3D/3dmodel.model@model@relationship#0: %v", errors.ErrOPCPartName), + fmt.Sprintf("/3D/3dmodel.model@model@relationship#1: %v", errors.ErrOPCPartName), + fmt.Sprintf("/3D/3dmodel.model@model@relationship#3: %v", errors.ErrOPCPartName), + fmt.Sprintf("/3D/3dmodel.model@model@relationship#4: %v", errors.ErrOPCRelTarget), + fmt.Sprintf("/3D/3dmodel.model@model@relationship#5: %v", errors.ErrOPCDuplicatedRel), + fmt.Sprintf("/3D/3dmodel.model@model@relationship#6: %v", errors.ErrOPCContentType), + fmt.Sprintf("/3D/3dmodel.model@model@relationship#7: %v", errors.ErrOPCDuplicatedRel), + fmt.Sprintf("/3D/3dmodel.model@model@relationship#7: %v", errors.ErrOPCContentType), + fmt.Sprintf("/3D/3dmodel.model@model@relationship#7: %v", errors.ErrOPCDuplicatedTicket), }}, {"namespaces", &Model{Extensions: []Extension{{Namespace: "fake", LocalName: "f", IsRequired: true}}}, []string{ - errors.ErrRequiredExt.Error(), + fmt.Sprintf("model: %v", errors.ErrRequiredExt), }}, {"metadata", &Model{Extensions: []Extension{{Namespace: "fake", LocalName: "f"}}, Metadata: []Metadata{ {Name: xml.Name{Space: "fake", Local: "issue"}}, {Name: xml.Name{Space: "f", Local: "issue"}}, {Name: xml.Name{Space: "fake", Local: "issue"}}, {Name: xml.Name{Local: "issue"}}, {}, }}, []string{ - fmt.Sprintf("metadata#1: %v", errors.ErrMetadataNamespace), - fmt.Sprintf("metadata#2: %v", errors.ErrMetadataDuplicated), - fmt.Sprintf("metadata#3: %v", errors.ErrMetadataName), - fmt.Sprintf("metadata#4: %v", &errors.MissingFieldError{Name: attrName}), + fmt.Sprintf("model@metadata#1: %v", errors.ErrMetadataNamespace), + fmt.Sprintf("model@metadata#2: %v", errors.ErrMetadataDuplicated), + fmt.Sprintf("model@metadata#3: %v", errors.ErrMetadataName), + fmt.Sprintf("model@metadata#4: %v", &errors.MissingFieldError{Name: attrName}), }}, {"build", &Model{Resources: Resources{Assets: []Asset{&BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}}, Objects: []*Object{ {ID: 2, Type: ObjectTypeOther, Mesh: &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, Triangles: Triangles{Triangle: []Triangle{ @@ -57,31 +57,31 @@ func TestValidate(t *testing.T) { {ObjectID: 100}, {ObjectID: 1, Metadata: MetadataGroup{Metadata: []Metadata{{Name: xml.Name{Local: "issue"}}}}}, }}}, []string{ - "Build: fake", - fmt.Sprintf("build@item#0: %v", &errors.MissingFieldError{Name: attrObjectID}), - fmt.Sprintf("build@item#1: %v", errors.ErrOtherItem), - fmt.Sprintf("build@item#2: %v", errors.ErrMissingResource), - fmt.Sprintf("build@item#3: %v", errors.ErrMissingResource), - fmt.Sprintf("build@item#3@metadata#0: %v", errors.ErrMetadataName), + "model: Build: fake", + fmt.Sprintf("model@build@item#0: %v", &errors.MissingFieldError{Name: attrObjectID}), + fmt.Sprintf("model@build@item#1: %v", errors.ErrOtherItem), + fmt.Sprintf("model@build@item#2: %v", errors.ErrMissingResource), + fmt.Sprintf("model@build@item#3: %v", errors.ErrMissingResource), + fmt.Sprintf("model@build@item#3@metadata#0: %v", errors.ErrMetadataName), }}, {"childs", &Model{Childs: map[string]*ChildModel{DefaultModelPath: {}, "/a.model": { Relationships: make([]Relationship, 1), Resources: Resources{Objects: []*Object{{}}}}}}, []string{ - errors.ErrOPCDuplicatedModelName.Error(), - fmt.Sprintf("/a.model@relationship#0: %v", errors.ErrOPCPartName), - fmt.Sprintf("/a.model@resources@object#0: %v", errors.ErrMissingID), - fmt.Sprintf("/a.model@resources@object#0: %v", errors.ErrInvalidObject), + fmt.Sprintf("model: %v", errors.ErrOPCDuplicatedModelName), + fmt.Sprintf("/a.model@model@relationship#0: %v", errors.ErrOPCPartName), + fmt.Sprintf("/a.model@model@resources@object#0: %v", errors.ErrMissingID), + fmt.Sprintf("/a.model@model@resources@object#0: %v", errors.ErrInvalidObject), }}, {"assets", &Model{Resources: Resources{Assets: []Asset{ &BaseMaterials{Materials: []Base{{Color: color.RGBA{}}}}, &BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}, &BaseMaterials{ID: 1}, }}}, []string{ - fmt.Sprintf("resources@basematerials#0: %v", errors.ErrMissingID), - fmt.Sprintf("resources@basematerials#0@base#0: %v", &errors.MissingFieldError{Name: attrName}), - fmt.Sprintf("resources@basematerials#0@base#0: %v", &errors.MissingFieldError{Name: attrDisplayColor}), - fmt.Sprintf("resources@basematerials#2: %v", errors.ErrDuplicatedID), - fmt.Sprintf("resources@basematerials#2: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model@resources@basematerials#0: %v", errors.ErrMissingID), + fmt.Sprintf("model@resources@basematerials#0@base#0: %v", &errors.MissingFieldError{Name: attrName}), + fmt.Sprintf("model@resources@basematerials#0@base#0: %v", &errors.MissingFieldError{Name: attrDisplayColor}), + fmt.Sprintf("model@resources@basematerials#2: %v", errors.ErrDuplicatedID), + fmt.Sprintf("model@resources@basematerials#2: %v", errors.ErrEmptyResourceProps), }}, {"objects", &Model{Resources: Resources{Assets: []Asset{ &BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}, {Name: "b", Color: color.RGBA{A: 1}}}}, @@ -104,29 +104,29 @@ func TestValidate(t *testing.T) { {V1: 1, V2: 2, V3: 3, PID: 100, P1: 0, P2: 0, P3: 0}, }}}}, }}}, []string{ - fmt.Sprintf("resources@object#0: %v", errors.ErrMissingID), - fmt.Sprintf("resources@object#0: %v", errors.ErrInvalidObject), - fmt.Sprintf("resources@object#1: %v", errors.ErrDuplicatedID), - fmt.Sprintf("resources@object#1: %v", &errors.MissingFieldError{Name: attrPID}), - fmt.Sprintf("resources@object#1: %v", errors.ErrInvalidObject), - fmt.Sprintf("resources@object#1@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("resources@object#1@mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("resources@object#1@components@component#0: %v", errors.ErrRecursion), - fmt.Sprintf("resources@object#3: %v", errors.ErrComponentsPID), - fmt.Sprintf("resources@object#3@components@component#0: %v", errors.ErrRecursion), - fmt.Sprintf("resources@object#3@components@component#2: %v", &errors.MissingFieldError{Name: attrObjectID}), - fmt.Sprintf("resources@object#3@components@component#3: %v", errors.ErrMissingResource), - fmt.Sprintf("resources@object#3@components@component#4: %v", errors.ErrMissingResource), - fmt.Sprintf("resources@object#4: %v", errors.ErrMissingResource), - fmt.Sprintf("resources@object#4@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("resources@object#4@mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("resources@object#4@mesh@triangle#0: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("resources@object#4@mesh@triangle#1: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("resources@object#4@mesh@triangle#2: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("resources@object#5: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("resources@object#5@mesh@triangle#0: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("resources@object#5@mesh@triangle#1: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("resources@object#5@mesh@triangle#3: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@object#0: %v", errors.ErrMissingID), + fmt.Sprintf("model@resources@object#0: %v", errors.ErrInvalidObject), + fmt.Sprintf("model@resources@object#1: %v", errors.ErrDuplicatedID), + fmt.Sprintf("model@resources@object#1: %v", &errors.MissingFieldError{Name: attrPID}), + fmt.Sprintf("model@resources@object#1: %v", errors.ErrInvalidObject), + fmt.Sprintf("model@resources@object#1@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("model@resources@object#1@mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("model@resources@object#1@components@component#0: %v", errors.ErrRecursion), + fmt.Sprintf("model@resources@object#3: %v", errors.ErrComponentsPID), + fmt.Sprintf("model@resources@object#3@components@component#0: %v", errors.ErrRecursion), + fmt.Sprintf("model@resources@object#3@components@component#2: %v", &errors.MissingFieldError{Name: attrObjectID}), + fmt.Sprintf("model@resources@object#3@components@component#3: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@object#3@components@component#4: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@object#4: %v", errors.ErrMissingResource), + fmt.Sprintf("model@resources@object#4@mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("model@resources@object#4@mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("model@resources@object#4@mesh@triangle#0: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("model@resources@object#4@mesh@triangle#1: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("model@resources@object#4@mesh@triangle#2: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("model@resources@object#5: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model@resources@object#5@mesh@triangle#0: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model@resources@object#5@mesh@triangle#1: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model@resources@object#5@mesh@triangle#3: %v", errors.ErrMissingResource), }}, } for _, tt := range tests { @@ -211,8 +211,8 @@ func TestModel_ValidateCoherency(t *testing.T) { }}, Childs: map[string]*ChildModel{"/other.model": {Resources: Resources{Objects: []*Object{ {Mesh: invalidMesh}, }}}}}, []string{ - fmt.Sprintf("/other.model@resources@object#0@mesh: %v", errors.ErrMeshConsistency), - fmt.Sprintf("resources@object#0@mesh: %v", errors.ErrMeshConsistency), + fmt.Sprintf("/other.model@model@resources@object#0@mesh: %v", errors.ErrMeshConsistency), + fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrMeshConsistency), }}, } for _, tt := range tests { From eb4857f566a808d09ea9a2ed67c6ec8812f643e8 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 13 Oct 2021 13:25:16 +0200 Subject: [PATCH 22/23] use slash as level separator --- beamlattice/decoder_test.go | 22 ++++---- beamlattice/validate_test.go | 36 ++++++------- errors/errors.go | 4 +- errors/list.go | 2 +- materials/decoder_test.go | 16 +++--- materials/validate_test.go | 56 +++++++++---------- production/decoder_test.go | 8 +-- production/validate_test.go | 34 ++++++------ read_test.go | 22 ++++---- slices/decoder_test.go | 20 +++---- slices/validate_test.go | 58 ++++++++++---------- validate_test.go | 102 +++++++++++++++++------------------ 12 files changed, 190 insertions(+), 190 deletions(-) diff --git a/beamlattice/decoder_test.go b/beamlattice/decoder_test.go index 2db8cea..3a01078 100644 --- a/beamlattice/decoder_test.go +++ b/beamlattice/decoder_test.go @@ -119,17 +119,17 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("radius", true)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("minlength", true)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("cap", false)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("clippingmode", false)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("clippingmesh", false)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", errors.NewParseAttrError("representationmesh", false)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beams@beam#0: %v", errors.NewParseAttrError("r1", false)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beams@beam#0: %v", errors.NewParseAttrError("r2", false)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beams@beam#2: %v", errors.NewParseAttrError("v2", true)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beams@beam#3: %v", errors.NewParseAttrError("v1", true)), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beamsets@beamset#0@ref#2: %v", errors.NewParseAttrError("index", true)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("radius", true)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("minlength", true)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("cap", false)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("clippingmode", false)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("clippingmesh", false)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("representationmesh", false)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beams/beam[0]: %v", errors.NewParseAttrError("r1", false)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beams/beam[0]: %v", errors.NewParseAttrError("r2", false)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beams/beam[2]: %v", errors.NewParseAttrError("v2", true)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beams/beam[3]: %v", errors.NewParseAttrError("v1", true)), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beamsets/beamset[0]/ref[2]: %v", errors.NewParseAttrError("index", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/beamlattice/validate_test.go b/beamlattice/validate_test.go index eecd791..e2e0b49 100644 --- a/beamlattice/validate_test.go +++ b/beamlattice/validate_test.go @@ -24,21 +24,21 @@ func TestValidate(t *testing.T) { {ID: 1, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{}}}}, }}}, }}, []string{ - fmt.Sprintf("/other.model@model@resources@object#0@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("/other.model@model@resources@object#0@mesh@beamlattice: %v", &errors.MissingFieldError{Name: attrMinLength}), - fmt.Sprintf("/other.model@model@resources@object#0@mesh@beamlattice: %v", &errors.MissingFieldError{Name: attrRadius}), - fmt.Sprintf("/other.model@model@resources@object#0@mesh@beamlattice: %v", ErrLatticeClippedNoMesh), + fmt.Sprintf("/other.model/model/resources/object[0]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("/other.model/model/resources/object[0]/mesh/beamlattice: %v", &errors.MissingFieldError{Name: attrMinLength}), + fmt.Sprintf("/other.model/model/resources/object[0]/mesh/beamlattice: %v", &errors.MissingFieldError{Name: attrRadius}), + fmt.Sprintf("/other.model/model/resources/object[0]/mesh/beamlattice: %v", ErrLatticeClippedNoMesh), }}, {"object without beamlattice", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Mesh: &go3mf.Mesh{}}, }}}, []string{ - fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrInsufficientTriangles), }}, {"object with components", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 2}}}}, }}}, []string{ - fmt.Sprintf("model@resources@object#0@components@component#0: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/object[0]/components/component[0]: %v", errors.ErrMissingResource), }}, {"object incorret type", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Type: go3mf.ObjectTypeOther, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{ @@ -51,9 +51,9 @@ func TestValidate(t *testing.T) { MinLength: 1, Radius: 1, ClipMode: ClipInside, }}}}, }}}, []string{ - fmt.Sprintf("model@resources@object#0@mesh@beamlattice: %v", ErrLatticeObjType), - fmt.Sprintf("model@resources@object#1@mesh@beamlattice: %v", ErrLatticeObjType), - fmt.Sprintf("model@resources@object#2@mesh@beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("model/resources/object[1]/mesh/beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("model/resources/object[2]/mesh/beamlattice: %v", ErrLatticeObjType), }}, {"incorrect mesh references", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{nil}}}, @@ -64,9 +64,9 @@ func TestValidate(t *testing.T) { MinLength: 1, Radius: 1, ClippingMeshID: 1, RepresentationMeshID: 2, }}}}, }}}, []string{ - fmt.Sprintf("model@resources@object#1@mesh@beamlattice: %v", errors.ErrMissingResource), - fmt.Sprintf("model@resources@object#1@mesh@beamlattice: %v", errors.ErrRecursion), - fmt.Sprintf("model@resources@object#2@mesh@beamlattice: %v", ErrLatticeInvalidMesh), + fmt.Sprintf("model/resources/object[1]/mesh/beamlattice: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/object[1]/mesh/beamlattice: %v", errors.ErrRecursion), + fmt.Sprintf("model/resources/object[2]/mesh/beamlattice: %v", ErrLatticeInvalidMesh), }}, {"incorrect beams", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ @@ -75,10 +75,10 @@ func TestValidate(t *testing.T) { }, }}}}}, }}}, []string{ - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beam#0: %v", ErrLatticeSameVertex), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beam#1: %v", ErrLatticeSameVertex), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beam#1: %v", ErrLatticeBeamR2), - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beam#2: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beam[0]: %v", ErrLatticeSameVertex), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beam[1]: %v", ErrLatticeSameVertex), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beam[1]: %v", ErrLatticeBeamR2), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beam[2]: %v", errors.ErrIndexOutOfBounds), }}, {"incorrect beamseat", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ @@ -87,7 +87,7 @@ func TestValidate(t *testing.T) { }}, BeamSets: BeamSets{BeamSet: []BeamSet{{Refs: []uint32{0, 2, 3}}}}, }}}}, }}}, []string{ - fmt.Sprintf("model@resources@object#0@mesh@beamlattice@beamset#0: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beamset[0]: %v", errors.ErrIndexOutOfBounds), }}, } for _, tt := range tests { diff --git a/errors/errors.go b/errors/errors.go index 0cbb5a0..0aaac44 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -47,7 +47,7 @@ func (l *Level) String() string { if l.Index == -1 { return l.Name } - return fmt.Sprintf("%s#%d", l.Name, l.Index) + return fmt.Sprintf("%s[%d]", l.Name, l.Index) } type Error struct { @@ -108,7 +108,7 @@ func (e *Error) Error() string { if e.Path == "" { levels = levels[1:] } - return fmt.Sprintf("%s: %v", strings.Join(levels, "@"), e.Err) + return fmt.Sprintf("%s: %v", strings.Join(levels, "/"), e.Err) } func NewMissingFieldError(name string) error { diff --git a/errors/list.go b/errors/list.go index 606d4e3..d8a73ea 100644 --- a/errors/list.go +++ b/errors/list.go @@ -57,7 +57,7 @@ func (e *List) Unwrap() error { // one. If any of the errs are errors.List, they will be flattened // one level into err. func Append(err error, errs ...error) error { - if errs == nil || len(errs) == 0 { + if len(errs) == 0 { return err } switch err := err.(type) { diff --git a/materials/decoder_test.go b/materials/decoder_test.go index bd7dd87..94ce9cf 100644 --- a/materials/decoder_test.go +++ b/materials/decoder_test.go @@ -63,14 +63,14 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("model@resources@texture2d#1: %v", errors.NewParseAttrError("id", true)), - fmt.Sprintf("model@resources@colorgroup#2@color#0: %v", errors.NewParseAttrError("color", true)), - fmt.Sprintf("model@resources@texture2dgroup#3: %v", errors.NewParseAttrError("texid", true)), - fmt.Sprintf("model@resources@texture2dgroup#3@tex2coord#0: %v", errors.NewParseAttrError("u", true)), - fmt.Sprintf("model@resources@texture2dgroup#3@tex2coord#1: %v", errors.NewParseAttrError("v", true)), - fmt.Sprintf("model@resources@compositematerials#4: %v", errors.NewParseAttrError("matid", true)), - fmt.Sprintf("model@resources@compositematerials#4@composite#1: %v", errors.NewParseAttrError("values", true)), - fmt.Sprintf("model@resources@multiproperties#5: %v", errors.NewParseAttrError("pids", true)), + fmt.Sprintf("model/resources/texture2d[1]: %v", errors.NewParseAttrError("id", true)), + fmt.Sprintf("model/resources/colorgroup[2]/color[0]: %v", errors.NewParseAttrError("color", true)), + fmt.Sprintf("model/resources/texture2dgroup[3]: %v", errors.NewParseAttrError("texid", true)), + fmt.Sprintf("model/resources/texture2dgroup[3]/tex2coord[0]: %v", errors.NewParseAttrError("u", true)), + fmt.Sprintf("model/resources/texture2dgroup[3]/tex2coord[1]: %v", errors.NewParseAttrError("v", true)), + fmt.Sprintf("model/resources/compositematerials[4]: %v", errors.NewParseAttrError("matid", true)), + fmt.Sprintf("model/resources/compositematerials[4]/composite[1]: %v", errors.NewParseAttrError("values", true)), + fmt.Sprintf("model/resources/multiproperties[5]: %v", errors.NewParseAttrError("pids", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/materials/validate_test.go b/materials/validate_test.go index 42ddbb1..7d927d7 100644 --- a/materials/validate_test.go +++ b/materials/validate_test.go @@ -27,10 +27,10 @@ func TestValidate(t *testing.T) { &MultiProperties{ID: 2}, }}}, }}, []string{ - fmt.Sprintf("/other.model@model@resources@colorgroup#0: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("/that.model@model@resources@multiproperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), - fmt.Sprintf("/that.model@model@resources@multiproperties#0: %v", ErrMultiBlend), - fmt.Sprintf("/that.model@model@resources@multiproperties#0: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("/other.model/model/resources/colorgroup[0]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("/that.model/model/resources/multiproperties[0]: %v", &errors.MissingFieldError{Name: attrPIDs}), + fmt.Sprintf("/that.model/model/resources/multiproperties[0]: %v", ErrMultiBlend), + fmt.Sprintf("/that.model/model/resources/multiproperties[0]: %v", errors.ErrEmptyResourceProps), }}, {"multi", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -48,15 +48,15 @@ func TestValidate(t *testing.T) { &MultiProperties{ID: 9, Multis: []Multi{{PIndices: []uint32{}}}, PIDs: []uint32{1, 3}}, }}, }, []string{ - fmt.Sprintf("model@resources@multiproperties#0: %v", &errors.MissingFieldError{Name: attrPIDs}), - fmt.Sprintf("model@resources@multiproperties#0: %v", ErrMultiBlend), - fmt.Sprintf("model@resources@multiproperties#0: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("model@resources@multiproperties#1: %v", ErrMultiRefMulti), - fmt.Sprintf("model@resources@multiproperties#1: %v", errors.ErrMissingResource), - fmt.Sprintf("model@resources@multiproperties#6@multi#0: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model@resources@multiproperties#7: %v", ErrMaterialMulti), - fmt.Sprintf("model@resources@multiproperties#7: %v", ErrMultiColors), - fmt.Sprintf("model@resources@multiproperties#8: %v", ErrMaterialMulti), + fmt.Sprintf("model/resources/multiproperties[0]: %v", &errors.MissingFieldError{Name: attrPIDs}), + fmt.Sprintf("model/resources/multiproperties[0]: %v", ErrMultiBlend), + fmt.Sprintf("model/resources/multiproperties[0]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model/resources/multiproperties[1]: %v", ErrMultiRefMulti), + fmt.Sprintf("model/resources/multiproperties[1]: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/multiproperties[6]/multi[0]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model/resources/multiproperties[7]: %v", ErrMaterialMulti), + fmt.Sprintf("model/resources/multiproperties[7]: %v", ErrMultiColors), + fmt.Sprintf("model/resources/multiproperties[8]: %v", ErrMaterialMulti), }}, {"missingTextPart", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -64,9 +64,9 @@ func TestValidate(t *testing.T) { &Texture2D{ID: 2, ContentType: TextureTypePNG, Path: "/a.png"}, }}, }, []string{ - fmt.Sprintf("model@resources@texture2d#0: %v", &errors.MissingFieldError{Name: attrPath}), - fmt.Sprintf("model@resources@texture2d#0: %v", &errors.MissingFieldError{Name: attrContentType}), - fmt.Sprintf("model@resources@texture2d#1: %v", ErrMissingTexturePart), + fmt.Sprintf("model/resources/texture2d[0]: %v", &errors.MissingFieldError{Name: attrPath}), + fmt.Sprintf("model/resources/texture2d[0]: %v", &errors.MissingFieldError{Name: attrContentType}), + fmt.Sprintf("model/resources/texture2d[1]: %v", ErrMissingTexturePart), }}, {"textureGroup", &go3mf.Model{ Attachments: []go3mf.Attachment{{Path: "/a.png"}}, @@ -78,10 +78,10 @@ func TestValidate(t *testing.T) { &Texture2DGroup{ID: 5, TextureID: 100, Coords: []TextureCoord{{}}}, }}, }, []string{ - fmt.Sprintf("model@resources@texture2dgroup#1: %v", &errors.MissingFieldError{Name: attrTexID}), - fmt.Sprintf("model@resources@texture2dgroup#1: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("model@resources@texture2dgroup#3: %v", ErrTextureReference), - fmt.Sprintf("model@resources@texture2dgroup#4: %v", ErrTextureReference), + fmt.Sprintf("model/resources/texture2dgroup[1]: %v", &errors.MissingFieldError{Name: attrTexID}), + fmt.Sprintf("model/resources/texture2dgroup[1]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model/resources/texture2dgroup[3]: %v", ErrTextureReference), + fmt.Sprintf("model/resources/texture2dgroup[4]: %v", ErrTextureReference), }}, {"colorGroup", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -90,8 +90,8 @@ func TestValidate(t *testing.T) { &ColorGroup{ID: 3, Colors: []color.RGBA{{R: 1}, {}}}, }}, }, []string{ - fmt.Sprintf("model@resources@colorgroup#0: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("model@resources@colorgroup#2@color#1: %v", &errors.MissingFieldError{Name: attrColor}), + fmt.Sprintf("model/resources/colorgroup[0]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model/resources/colorgroup[2]/color[1]: %v", &errors.MissingFieldError{Name: attrColor}), }}, {"composite", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -105,12 +105,12 @@ func TestValidate(t *testing.T) { &CompositeMaterials{ID: 5, MaterialID: 2, Indices: []uint32{0, 1}, Composites: []Composite{{Values: []float32{1, 2}}}}, &CompositeMaterials{ID: 6, MaterialID: 100, Indices: []uint32{0, 1}, Composites: []Composite{{Values: []float32{1, 2}}}}, }}}, []string{ - fmt.Sprintf("model@resources@compositematerials#1: %v", &errors.MissingFieldError{Name: attrMatID}), - fmt.Sprintf("model@resources@compositematerials#1: %v", &errors.MissingFieldError{Name: attrMatIndices}), - fmt.Sprintf("model@resources@compositematerials#1: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("model@resources@compositematerials#3: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model@resources@compositematerials#4: %v", ErrCompositeBase), - fmt.Sprintf("model@resources@compositematerials#5: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/compositematerials[1]: %v", &errors.MissingFieldError{Name: attrMatID}), + fmt.Sprintf("model/resources/compositematerials[1]: %v", &errors.MissingFieldError{Name: attrMatIndices}), + fmt.Sprintf("model/resources/compositematerials[1]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model/resources/compositematerials[3]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model/resources/compositematerials[4]: %v", ErrCompositeBase), + fmt.Sprintf("model/resources/compositematerials[5]: %v", errors.ErrMissingResource), }}, } for _, tt := range tests { diff --git a/production/decoder_test.go b/production/decoder_test.go index 7708820..d7ea7c4 100644 --- a/production/decoder_test.go +++ b/production/decoder_test.go @@ -73,10 +73,10 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("model@resources@object#1: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), - fmt.Sprintf("model@resources@object#1@components@component#0: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), - fmt.Sprintf("model@build: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), - fmt.Sprintf("model@build@item#0: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("model/resources/object[1]: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("model/resources/object[1]/components/component[0]: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("model/build: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("model/build/item[0]: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/production/validate_test.go b/production/validate_test.go index 3323a5d..0638f53 100644 --- a/production/validate_test.go +++ b/production/validate_test.go @@ -23,15 +23,15 @@ func TestValidate(t *testing.T) { want []string }{ {"buildNoUUID", &go3mf.Model{Build: go3mf.Build{}}, []string{ - fmt.Sprintf("model@build: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model/build: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"buildEmptyUUID", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{}}}}, []string{ - fmt.Sprintf("model@build: %v", ErrUUID), + fmt.Sprintf("model/build: %v", ErrUUID), }}, {"buildNonValidUUID", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{"a-b-c-d"}}}}, []string{ - fmt.Sprintf("model@build: %v", ErrUUID), + fmt.Sprintf("model/build: %v", ErrUUID), }}, {"extReq", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, @@ -43,7 +43,7 @@ func TestValidate(t *testing.T) { AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ {ObjectID: 1, AnyAttr: spec.AnyAttr{&ItemAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d478", Path: "/other.model"}}}, }}}, []string{ - fmt.Sprintf("/other.model@model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model/model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"items", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ @@ -54,11 +54,11 @@ func TestValidate(t *testing.T) { }}, Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, Resources: go3mf.Resources{Objects: []*go3mf.Object{{ID: 1, Mesh: validMesh.Mesh}}}}, []string{ - fmt.Sprintf("model@build@item#1: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("model@build@item#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("model@build@item#3: %v", ErrUUID), - fmt.Sprintf("/other.model@model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model/build/item[1]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model/build/item[2]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model/build/item[3]: %v", ErrUUID), + fmt.Sprintf("/other.model/model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"components", &go3mf.Model{Resources: go3mf.Resources{ Objects: []*go3mf.Object{ @@ -70,10 +70,10 @@ func TestValidate(t *testing.T) { }}}, }, }, Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}}, []string{ - fmt.Sprintf("model@resources@object#0: %v", ErrUUID), - fmt.Sprintf("model@resources@object#1@components@component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("model@resources@object#1@components@component#1: %v", ErrUUID), - fmt.Sprintf("model@resources@object#1@components@component#2: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model/resources/object[0]: %v", ErrUUID), + fmt.Sprintf("model/resources/object[1]/components/component[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("model/resources/object[1]/components/component[1]: %v", ErrUUID), + fmt.Sprintf("model/resources/object[1]/components/component[2]: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"child", &go3mf.Model{Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}, Childs: map[string]*go3mf.ChildModel{ @@ -83,10 +83,10 @@ func TestValidate(t *testing.T) { {ObjectID: 1, AnyAttr: spec.AnyAttr{&ComponentAttr{Path: "/b.model"}}}, }}}, }}}}}, []string{ - fmt.Sprintf("/b.model@model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model@model@resources@object#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model@model@resources@object#0@components@component#0: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model@model@resources@object#0@components@component#0: %v", ErrProdRefInNonRoot), + fmt.Sprintf("/b.model/model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model/model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model/model/resources/object[0]/components/component[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("/other.model/model/resources/object[0]/components/component[0]: %v", ErrProdRefInNonRoot), }}, } for _, tt := range tests { diff --git a/read_test.go b/read_test.go index 6d516bf..eed2aad 100644 --- a/read_test.go +++ b/read_test.go @@ -650,17 +650,17 @@ func TestNewDecoder(t *testing.T) { func TestDecoder_processRootModel_warns(t *testing.T) { spec.Register(fakeSpec.Namespace, new(qmExtension)) want := []string{ - fmt.Sprintf("model@resources@basematerials#0@base#0: %v", specerr.NewParseAttrError("displaycolor", true)), - fmt.Sprintf("model@resources@basematerials#1: %v", specerr.NewParseAttrError("id", true)), - fmt.Sprintf("model@resources@object#0@mesh@vertices@vertex#8: %v", specerr.NewParseAttrError("x", true)), - fmt.Sprintf("model@resources@object#0@mesh@triangles@triangle#13: %v", specerr.NewParseAttrError("v1", true)), - fmt.Sprintf("model@resources@object#1: %v", specerr.NewParseAttrError("pid", false)), - fmt.Sprintf("model@resources@object#1: %v", specerr.NewParseAttrError("pindex", false)), - fmt.Sprintf("model@resources@object#1: %v", specerr.NewParseAttrError("type", false)), - fmt.Sprintf("model@resources@object#2@components@component#0: %v", specerr.NewParseAttrError("transform", false)), - fmt.Sprintf("model@resources@object#2@components@component#1: %v", specerr.NewParseAttrError("objectid", true)), - fmt.Sprintf("model@build@item#0: %v", specerr.NewParseAttrError("transform", false)), - fmt.Sprintf("model@build@item#3: %v", specerr.NewParseAttrError("objectid", true)), + fmt.Sprintf("model/resources/basematerials[0]/base[0]: %v", specerr.NewParseAttrError("displaycolor", true)), + fmt.Sprintf("model/resources/basematerials[1]: %v", specerr.NewParseAttrError("id", true)), + fmt.Sprintf("model/resources/object[0]/mesh/vertices/vertex[8]: %v", specerr.NewParseAttrError("x", true)), + fmt.Sprintf("model/resources/object[0]/mesh/triangles/triangle[13]: %v", specerr.NewParseAttrError("v1", true)), + fmt.Sprintf("model/resources/object[1]: %v", specerr.NewParseAttrError("pid", false)), + fmt.Sprintf("model/resources/object[1]: %v", specerr.NewParseAttrError("pindex", false)), + fmt.Sprintf("model/resources/object[1]: %v", specerr.NewParseAttrError("type", false)), + fmt.Sprintf("model/resources/object[2]/components/component[0]: %v", specerr.NewParseAttrError("transform", false)), + fmt.Sprintf("model/resources/object[2]/components/component[1]: %v", specerr.NewParseAttrError("objectid", true)), + fmt.Sprintf("model/build/item[0]: %v", specerr.NewParseAttrError("transform", false)), + fmt.Sprintf("model/build/item[3]: %v", specerr.NewParseAttrError("objectid", true)), } got := new(Model) got.Extensions = append(got.Extensions, fakeSpec) diff --git a/slices/decoder_test.go b/slices/decoder_test.go index 9c7e0eb..58f764c 100644 --- a/slices/decoder_test.go +++ b/slices/decoder_test.go @@ -95,16 +95,16 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("model@resources@slicestack#0: %v", specerr.NewParseAttrError("id", true)), - fmt.Sprintf("model@resources@slicestack#0: %v", specerr.NewParseAttrError("zbottom", false)), - fmt.Sprintf("model@resources@slicestack#0@slice#0@vertices@vertex#0: %v", specerr.NewParseAttrError("x", true)), - fmt.Sprintf("model@resources@slicestack#0@slice#0@vertices@vertex#1: %v", specerr.NewParseAttrError("y", true)), - fmt.Sprintf("model@resources@slicestack#0@slice#1: %v", specerr.NewParseAttrError("ztop", true)), - fmt.Sprintf("model@resources@slicestack#0@slice#1@polygon#0: %v", specerr.NewParseAttrError("startv", true)), - fmt.Sprintf("model@resources@slicestack#0@slice#1@polygon#0@segment#1: %v", specerr.NewParseAttrError("v2", true)), - fmt.Sprintf("model@resources@slicestack#0@sliceref#0: %v", specerr.NewParseAttrError("slicestackid", true)), - fmt.Sprintf("model@resources@object#0: %v", specerr.NewParseAttrError("meshresolution", false)), - fmt.Sprintf("model@resources@object#0: %v", specerr.NewParseAttrError("slicestackid", true)), + fmt.Sprintf("model/resources/slicestack[0]: %v", specerr.NewParseAttrError("id", true)), + fmt.Sprintf("model/resources/slicestack[0]: %v", specerr.NewParseAttrError("zbottom", false)), + fmt.Sprintf("model/resources/slicestack[0]/slice[0]/vertices/vertex[0]: %v", specerr.NewParseAttrError("x", true)), + fmt.Sprintf("model/resources/slicestack[0]/slice[0]/vertices/vertex[1]: %v", specerr.NewParseAttrError("y", true)), + fmt.Sprintf("model/resources/slicestack[0]/slice[1]: %v", specerr.NewParseAttrError("ztop", true)), + fmt.Sprintf("model/resources/slicestack[0]/slice[1]/polygon[0]: %v", specerr.NewParseAttrError("startv", true)), + fmt.Sprintf("model/resources/slicestack[0]/slice[1]/polygon[0]/segment[1]: %v", specerr.NewParseAttrError("v2", true)), + fmt.Sprintf("model/resources/slicestack[0]/sliceref[0]: %v", specerr.NewParseAttrError("slicestackid", true)), + fmt.Sprintf("model/resources/object[0]: %v", specerr.NewParseAttrError("meshresolution", false)), + fmt.Sprintf("model/resources/object[0]: %v", specerr.NewParseAttrError("slicestackid", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/slices/validate_test.go b/slices/validate_test.go index 8714c5f..799fcf5 100644 --- a/slices/validate_test.go +++ b/slices/validate_test.go @@ -33,7 +33,7 @@ func TestValidate(t *testing.T) { }}}, }}, }, []string{ - fmt.Sprintf("model@resources@object#0: %v", errors.ErrInvalidObject), + fmt.Sprintf("model/resources/object[0]: %v", errors.ErrInvalidObject), }}, {"child", &go3mf.Model{Childs: map[string]*go3mf.ChildModel{ "/other.model": {Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -43,8 +43,8 @@ func TestValidate(t *testing.T) { &SliceStack{ID: 2}, }}}, }}, []string{ - fmt.Sprintf("/other.model@model@resources@slicestack#0: %v", ErrSlicesAndRefs), - fmt.Sprintf("/that.model@model@resources@slicestack#0: %v", ErrSlicesAndRefs), + fmt.Sprintf("/other.model/model/resources/slicestack[0]: %v", ErrSlicesAndRefs), + fmt.Sprintf("/that.model/model/resources/slicestack[0]: %v", ErrSlicesAndRefs), }}, {"slicestack", &go3mf.Model{Resources: go3mf.Resources{ Assets: []go3mf.Asset{&SliceStack{ @@ -60,13 +60,13 @@ func TestValidate(t *testing.T) { }, }}, }}, []string{ - fmt.Sprintf("model@resources@slicestack#0@slice#0: %v", &errors.MissingFieldError{Name: attrZTop}), - fmt.Sprintf("model@resources@slicestack#0@slice#1: %v", ErrSliceSmallTopZ), - fmt.Sprintf("model@resources@slicestack#0@slice#1: %v", ErrSliceInsufficientVertices), - fmt.Sprintf("model@resources@slicestack#0@slice#1: %v", ErrSliceInsufficientPolygons), - fmt.Sprintf("model@resources@slicestack#0@slice#2@polygon#0: %v", ErrSliceInsufficientSegments), - fmt.Sprintf("model@resources@slicestack#0@slice#3: %v", ErrSliceNoMonotonic), - fmt.Sprintf("model@resources@slicestack#0@slice#4: %v", ErrSliceNoMonotonic), + fmt.Sprintf("model/resources/slicestack[0]/slice[0]: %v", &errors.MissingFieldError{Name: attrZTop}), + fmt.Sprintf("model/resources/slicestack[0]/slice[1]: %v", ErrSliceSmallTopZ), + fmt.Sprintf("model/resources/slicestack[0]/slice[1]: %v", ErrSliceInsufficientVertices), + fmt.Sprintf("model/resources/slicestack[0]/slice[1]: %v", ErrSliceInsufficientPolygons), + fmt.Sprintf("model/resources/slicestack[0]/slice[2]/polygon[0]: %v", ErrSliceInsufficientSegments), + fmt.Sprintf("model/resources/slicestack[0]/slice[3]: %v", ErrSliceNoMonotonic), + fmt.Sprintf("model/resources/slicestack[0]/slice[4]: %v", ErrSliceNoMonotonic), }}, {"sliceref", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{ @@ -91,13 +91,13 @@ func TestValidate(t *testing.T) { }, }}, }}, []string{ - fmt.Sprintf("model@resources@slicestack#1@sliceref#0: %v", &errors.MissingFieldError{Name: attrSlicePath}), - fmt.Sprintf("model@resources@slicestack#1@sliceref#0: %v", &errors.MissingFieldError{Name: attrSliceRefID}), - fmt.Sprintf("model@resources@slicestack#1@sliceref#1: %v", ErrSliceRefSamePart), - fmt.Sprintf("model@resources@slicestack#1@sliceref#2: %v", errors.ErrMissingResource), - fmt.Sprintf("model@resources@slicestack#1@sliceref#3: %v", ErrSliceRefRef), - fmt.Sprintf("model@resources@slicestack#1@sliceref#4: %v", ErrNonSliceStack), - fmt.Sprintf("model@resources@slicestack#1@sliceref#6: %v", ErrSliceNoMonotonic), + fmt.Sprintf("model/resources/slicestack[1]/sliceref[0]: %v", &errors.MissingFieldError{Name: attrSlicePath}), + fmt.Sprintf("model/resources/slicestack[1]/sliceref[0]: %v", &errors.MissingFieldError{Name: attrSliceRefID}), + fmt.Sprintf("model/resources/slicestack[1]/sliceref[1]: %v", ErrSliceRefSamePart), + fmt.Sprintf("model/resources/slicestack[1]/sliceref[2]: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/slicestack[1]/sliceref[3]: %v", ErrSliceRefRef), + fmt.Sprintf("model/resources/slicestack[1]/sliceref[4]: %v", ErrNonSliceStack), + fmt.Sprintf("model/resources/slicestack[1]/sliceref[6]: %v", ErrSliceNoMonotonic), }}, {"info", &go3mf.Model{Build: go3mf.Build{Items: []*go3mf.Item{ {ObjectID: 7}, @@ -153,18 +153,18 @@ func TestValidate(t *testing.T) { SliceStackID: 11, }}}, }}}, []string{ - fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("model@resources@object#0: %v", errors.ErrMissingResource), - fmt.Sprintf("model@resources@object#1: %v", ErrSliceInvalidTranform), - fmt.Sprintf("model@resources@object#1: %v", ErrSliceExtRequired), - fmt.Sprintf("model@resources@object#2: %v", &errors.MissingFieldError{Name: attrSliceRefID}), - fmt.Sprintf("model@resources@object#3: %v", ErrNonSliceStack), - fmt.Sprintf("model@resources@object#4: %v", ErrSliceInvalidTranform), - fmt.Sprintf("model@resources@object#4: %v", ErrSlicePolygonNotClosed), - fmt.Sprintf("model@resources@object#5: %v", ErrSliceInvalidTranform), - fmt.Sprintf("model@resources@object#5: %v", ErrSlicePolygonNotClosed), - fmt.Sprintf("model@resources@object#6@components@component#1: %v", errors.ErrRecursion), + fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("model/resources/object[0]: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/object[1]: %v", ErrSliceInvalidTranform), + fmt.Sprintf("model/resources/object[1]: %v", ErrSliceExtRequired), + fmt.Sprintf("model/resources/object[2]: %v", &errors.MissingFieldError{Name: attrSliceRefID}), + fmt.Sprintf("model/resources/object[3]: %v", ErrNonSliceStack), + fmt.Sprintf("model/resources/object[4]: %v", ErrSliceInvalidTranform), + fmt.Sprintf("model/resources/object[4]: %v", ErrSlicePolygonNotClosed), + fmt.Sprintf("model/resources/object[5]: %v", ErrSliceInvalidTranform), + fmt.Sprintf("model/resources/object[5]: %v", ErrSlicePolygonNotClosed), + fmt.Sprintf("model/resources/object[6]/components/component[1]: %v", errors.ErrRecursion), }}, } for _, tt := range tests { diff --git a/validate_test.go b/validate_test.go index 9784700..44ef45e 100644 --- a/validate_test.go +++ b/validate_test.go @@ -27,15 +27,15 @@ func TestValidate(t *testing.T) { {}, {Path: "/.png"}, {Path: "/a.png"}, {Path: "a.png"}, {Path: "/b.png"}, {Path: "/a.png"}, {Path: "/a.png", Type: RelTypePrintTicket}, {Path: "/a.png", Type: RelTypePrintTicket}, }}, []string{ - fmt.Sprintf("/3D/3dmodel.model@model@relationship#0: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model@model@relationship#1: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model@model@relationship#3: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model@model@relationship#4: %v", errors.ErrOPCRelTarget), - fmt.Sprintf("/3D/3dmodel.model@model@relationship#5: %v", errors.ErrOPCDuplicatedRel), - fmt.Sprintf("/3D/3dmodel.model@model@relationship#6: %v", errors.ErrOPCContentType), - fmt.Sprintf("/3D/3dmodel.model@model@relationship#7: %v", errors.ErrOPCDuplicatedRel), - fmt.Sprintf("/3D/3dmodel.model@model@relationship#7: %v", errors.ErrOPCContentType), - fmt.Sprintf("/3D/3dmodel.model@model@relationship#7: %v", errors.ErrOPCDuplicatedTicket), + fmt.Sprintf("/3D/3dmodel.model/model/relationship[0]: %v", errors.ErrOPCPartName), + fmt.Sprintf("/3D/3dmodel.model/model/relationship[1]: %v", errors.ErrOPCPartName), + fmt.Sprintf("/3D/3dmodel.model/model/relationship[3]: %v", errors.ErrOPCPartName), + fmt.Sprintf("/3D/3dmodel.model/model/relationship[4]: %v", errors.ErrOPCRelTarget), + fmt.Sprintf("/3D/3dmodel.model/model/relationship[5]: %v", errors.ErrOPCDuplicatedRel), + fmt.Sprintf("/3D/3dmodel.model/model/relationship[6]: %v", errors.ErrOPCContentType), + fmt.Sprintf("/3D/3dmodel.model/model/relationship[7]: %v", errors.ErrOPCDuplicatedRel), + fmt.Sprintf("/3D/3dmodel.model/model/relationship[7]: %v", errors.ErrOPCContentType), + fmt.Sprintf("/3D/3dmodel.model/model/relationship[7]: %v", errors.ErrOPCDuplicatedTicket), }}, {"namespaces", &Model{Extensions: []Extension{{Namespace: "fake", LocalName: "f", IsRequired: true}}}, []string{ fmt.Sprintf("model: %v", errors.ErrRequiredExt), @@ -43,10 +43,10 @@ func TestValidate(t *testing.T) { {"metadata", &Model{Extensions: []Extension{{Namespace: "fake", LocalName: "f"}}, Metadata: []Metadata{ {Name: xml.Name{Space: "fake", Local: "issue"}}, {Name: xml.Name{Space: "f", Local: "issue"}}, {Name: xml.Name{Space: "fake", Local: "issue"}}, {Name: xml.Name{Local: "issue"}}, {}, }}, []string{ - fmt.Sprintf("model@metadata#1: %v", errors.ErrMetadataNamespace), - fmt.Sprintf("model@metadata#2: %v", errors.ErrMetadataDuplicated), - fmt.Sprintf("model@metadata#3: %v", errors.ErrMetadataName), - fmt.Sprintf("model@metadata#4: %v", &errors.MissingFieldError{Name: attrName}), + fmt.Sprintf("model/metadata[1]: %v", errors.ErrMetadataNamespace), + fmt.Sprintf("model/metadata[2]: %v", errors.ErrMetadataDuplicated), + fmt.Sprintf("model/metadata[3]: %v", errors.ErrMetadataName), + fmt.Sprintf("model/metadata[4]: %v", &errors.MissingFieldError{Name: attrName}), }}, {"build", &Model{Resources: Resources{Assets: []Asset{&BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}}, Objects: []*Object{ {ID: 2, Type: ObjectTypeOther, Mesh: &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, Triangles: Triangles{Triangle: []Triangle{ @@ -58,30 +58,30 @@ func TestValidate(t *testing.T) { {ObjectID: 1, Metadata: MetadataGroup{Metadata: []Metadata{{Name: xml.Name{Local: "issue"}}}}}, }}}, []string{ "model: Build: fake", - fmt.Sprintf("model@build@item#0: %v", &errors.MissingFieldError{Name: attrObjectID}), - fmt.Sprintf("model@build@item#1: %v", errors.ErrOtherItem), - fmt.Sprintf("model@build@item#2: %v", errors.ErrMissingResource), - fmt.Sprintf("model@build@item#3: %v", errors.ErrMissingResource), - fmt.Sprintf("model@build@item#3@metadata#0: %v", errors.ErrMetadataName), + fmt.Sprintf("model/build/item[0]: %v", &errors.MissingFieldError{Name: attrObjectID}), + fmt.Sprintf("model/build/item[1]: %v", errors.ErrOtherItem), + fmt.Sprintf("model/build/item[2]: %v", errors.ErrMissingResource), + fmt.Sprintf("model/build/item[3]: %v", errors.ErrMissingResource), + fmt.Sprintf("model/build/item[3]/metadata[0]: %v", errors.ErrMetadataName), }}, {"childs", &Model{Childs: map[string]*ChildModel{DefaultModelPath: {}, "/a.model": { Relationships: make([]Relationship, 1), Resources: Resources{Objects: []*Object{{}}}}}}, []string{ fmt.Sprintf("model: %v", errors.ErrOPCDuplicatedModelName), - fmt.Sprintf("/a.model@model@relationship#0: %v", errors.ErrOPCPartName), - fmt.Sprintf("/a.model@model@resources@object#0: %v", errors.ErrMissingID), - fmt.Sprintf("/a.model@model@resources@object#0: %v", errors.ErrInvalidObject), + fmt.Sprintf("/a.model/model/relationship[0]: %v", errors.ErrOPCPartName), + fmt.Sprintf("/a.model/model/resources/object[0]: %v", errors.ErrMissingID), + fmt.Sprintf("/a.model/model/resources/object[0]: %v", errors.ErrInvalidObject), }}, {"assets", &Model{Resources: Resources{Assets: []Asset{ &BaseMaterials{Materials: []Base{{Color: color.RGBA{}}}}, &BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}, &BaseMaterials{ID: 1}, }}}, []string{ - fmt.Sprintf("model@resources@basematerials#0: %v", errors.ErrMissingID), - fmt.Sprintf("model@resources@basematerials#0@base#0: %v", &errors.MissingFieldError{Name: attrName}), - fmt.Sprintf("model@resources@basematerials#0@base#0: %v", &errors.MissingFieldError{Name: attrDisplayColor}), - fmt.Sprintf("model@resources@basematerials#2: %v", errors.ErrDuplicatedID), - fmt.Sprintf("model@resources@basematerials#2: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("model/resources/basematerials[0]: %v", errors.ErrMissingID), + fmt.Sprintf("model/resources/basematerials[0]/base[0]: %v", &errors.MissingFieldError{Name: attrName}), + fmt.Sprintf("model/resources/basematerials[0]/base[0]: %v", &errors.MissingFieldError{Name: attrDisplayColor}), + fmt.Sprintf("model/resources/basematerials[2]: %v", errors.ErrDuplicatedID), + fmt.Sprintf("model/resources/basematerials[2]: %v", errors.ErrEmptyResourceProps), }}, {"objects", &Model{Resources: Resources{Assets: []Asset{ &BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}, {Name: "b", Color: color.RGBA{A: 1}}}}, @@ -104,29 +104,29 @@ func TestValidate(t *testing.T) { {V1: 1, V2: 2, V3: 3, PID: 100, P1: 0, P2: 0, P3: 0}, }}}}, }}}, []string{ - fmt.Sprintf("model@resources@object#0: %v", errors.ErrMissingID), - fmt.Sprintf("model@resources@object#0: %v", errors.ErrInvalidObject), - fmt.Sprintf("model@resources@object#1: %v", errors.ErrDuplicatedID), - fmt.Sprintf("model@resources@object#1: %v", &errors.MissingFieldError{Name: attrPID}), - fmt.Sprintf("model@resources@object#1: %v", errors.ErrInvalidObject), - fmt.Sprintf("model@resources@object#1@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("model@resources@object#1@mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("model@resources@object#1@components@component#0: %v", errors.ErrRecursion), - fmt.Sprintf("model@resources@object#3: %v", errors.ErrComponentsPID), - fmt.Sprintf("model@resources@object#3@components@component#0: %v", errors.ErrRecursion), - fmt.Sprintf("model@resources@object#3@components@component#2: %v", &errors.MissingFieldError{Name: attrObjectID}), - fmt.Sprintf("model@resources@object#3@components@component#3: %v", errors.ErrMissingResource), - fmt.Sprintf("model@resources@object#3@components@component#4: %v", errors.ErrMissingResource), - fmt.Sprintf("model@resources@object#4: %v", errors.ErrMissingResource), - fmt.Sprintf("model@resources@object#4@mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("model@resources@object#4@mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("model@resources@object#4@mesh@triangle#0: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("model@resources@object#4@mesh@triangle#1: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("model@resources@object#4@mesh@triangle#2: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("model@resources@object#5: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model@resources@object#5@mesh@triangle#0: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model@resources@object#5@mesh@triangle#1: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model@resources@object#5@mesh@triangle#3: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/object[0]: %v", errors.ErrMissingID), + fmt.Sprintf("model/resources/object[0]: %v", errors.ErrInvalidObject), + fmt.Sprintf("model/resources/object[1]: %v", errors.ErrDuplicatedID), + fmt.Sprintf("model/resources/object[1]: %v", &errors.MissingFieldError{Name: attrPID}), + fmt.Sprintf("model/resources/object[1]: %v", errors.ErrInvalidObject), + fmt.Sprintf("model/resources/object[1]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("model/resources/object[1]/mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("model/resources/object[1]/components/component[0]: %v", errors.ErrRecursion), + fmt.Sprintf("model/resources/object[3]: %v", errors.ErrComponentsPID), + fmt.Sprintf("model/resources/object[3]/components/component[0]: %v", errors.ErrRecursion), + fmt.Sprintf("model/resources/object[3]/components/component[2]: %v", &errors.MissingFieldError{Name: attrObjectID}), + fmt.Sprintf("model/resources/object[3]/components/component[3]: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/object[3]/components/component[4]: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/object[4]: %v", errors.ErrMissingResource), + fmt.Sprintf("model/resources/object[4]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("model/resources/object[4]/mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("model/resources/object[4]/mesh/triangle[0]: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("model/resources/object[4]/mesh/triangle[1]: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("model/resources/object[4]/mesh/triangle[2]: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("model/resources/object[5]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model/resources/object[5]/mesh/triangle[0]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model/resources/object[5]/mesh/triangle[1]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("model/resources/object[5]/mesh/triangle[3]: %v", errors.ErrMissingResource), }}, } for _, tt := range tests { @@ -211,8 +211,8 @@ func TestModel_ValidateCoherency(t *testing.T) { }}, Childs: map[string]*ChildModel{"/other.model": {Resources: Resources{Objects: []*Object{ {Mesh: invalidMesh}, }}}}}, []string{ - fmt.Sprintf("/other.model@model@resources@object#0@mesh: %v", errors.ErrMeshConsistency), - fmt.Sprintf("model@resources@object#0@mesh: %v", errors.ErrMeshConsistency), + fmt.Sprintf("/other.model/model/resources/object[0]/mesh: %v", errors.ErrMeshConsistency), + fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrMeshConsistency), }}, } for _, tt := range tests { From 207a8cb0fdb937c30aa0290cac9e5af8bcbf7465 Mon Sep 17 00:00:00 2001 From: qmuntal Date: Wed, 13 Oct 2021 13:47:06 +0200 Subject: [PATCH 23/23] improve error reporting --- beamlattice/decoder_test.go | 22 +++---- beamlattice/validate_test.go | 36 ++++++------ errors/errors.go | 15 +++-- materials/decoder_test.go | 16 +++--- materials/validate_test.go | 56 +++++++++--------- production/decoder_test.go | 8 +-- production/validate_test.go | 34 +++++------ read_test.go | 22 +++---- slices/decoder_test.go | 20 +++---- slices/validate_test.go | 58 +++++++++---------- validate_test.go | 108 +++++++++++++++++------------------ 11 files changed, 199 insertions(+), 196 deletions(-) diff --git a/beamlattice/decoder_test.go b/beamlattice/decoder_test.go index 3a01078..58614c3 100644 --- a/beamlattice/decoder_test.go +++ b/beamlattice/decoder_test.go @@ -119,17 +119,17 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("radius", true)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("minlength", true)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("cap", false)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("clippingmode", false)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("clippingmesh", false)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("representationmesh", false)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beams/beam[0]: %v", errors.NewParseAttrError("r1", false)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beams/beam[0]: %v", errors.NewParseAttrError("r2", false)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beams/beam[2]: %v", errors.NewParseAttrError("v2", true)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beams/beam[3]: %v", errors.NewParseAttrError("v1", true)), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beamsets/beamset[0]/ref[2]: %v", errors.NewParseAttrError("index", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("radius", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("minlength", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("cap", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("clippingmode", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("clippingmesh", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice: %v", errors.NewParseAttrError("representationmesh", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beams/beam[0]: %v", errors.NewParseAttrError("r1", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beams/beam[0]: %v", errors.NewParseAttrError("r2", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beams/beam[2]: %v", errors.NewParseAttrError("v2", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beams/beam[3]: %v", errors.NewParseAttrError("v1", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beamsets/beamset[0]/ref[2]: %v", errors.NewParseAttrError("index", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/beamlattice/validate_test.go b/beamlattice/validate_test.go index e2e0b49..6ce90c7 100644 --- a/beamlattice/validate_test.go +++ b/beamlattice/validate_test.go @@ -24,21 +24,21 @@ func TestValidate(t *testing.T) { {ID: 1, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{}}}}, }}}, }}, []string{ - fmt.Sprintf("/other.model/model/resources/object[0]/mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("/other.model/model/resources/object[0]/mesh/beamlattice: %v", &errors.MissingFieldError{Name: attrMinLength}), - fmt.Sprintf("/other.model/model/resources/object[0]/mesh/beamlattice: %v", &errors.MissingFieldError{Name: attrRadius}), - fmt.Sprintf("/other.model/model/resources/object[0]/mesh/beamlattice: %v", ErrLatticeClippedNoMesh), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]/mesh/beamlattice: %v", &errors.MissingFieldError{Name: attrMinLength}), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]/mesh/beamlattice: %v", &errors.MissingFieldError{Name: attrRadius}), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]/mesh/beamlattice: %v", ErrLatticeClippedNoMesh), }}, {"object without beamlattice", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Mesh: &go3mf.Mesh{}}, }}}, []string{ - fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh: %v", errors.ErrInsufficientTriangles), }}, {"object with components", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Components: &go3mf.Components{Component: []*go3mf.Component{{ObjectID: 2}}}}, }}}, []string{ - fmt.Sprintf("model/resources/object[0]/components/component[0]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/components/component[0]: %v", errors.ErrMissingResource), }}, {"object incorret type", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Type: go3mf.ObjectTypeOther, Mesh: &go3mf.Mesh{Any: spec.Any{&BeamLattice{ @@ -51,9 +51,9 @@ func TestValidate(t *testing.T) { MinLength: 1, Radius: 1, ClipMode: ClipInside, }}}}, }}}, []string{ - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice: %v", ErrLatticeObjType), - fmt.Sprintf("model/resources/object[1]/mesh/beamlattice: %v", ErrLatticeObjType), - fmt.Sprintf("model/resources/object[2]/mesh/beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/mesh/beamlattice: %v", ErrLatticeObjType), + fmt.Sprintf("go3mf: XPath: /model/resources/object[2]/mesh/beamlattice: %v", ErrLatticeObjType), }}, {"incorrect mesh references", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 1, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{nil}}}, @@ -64,9 +64,9 @@ func TestValidate(t *testing.T) { MinLength: 1, Radius: 1, ClippingMeshID: 1, RepresentationMeshID: 2, }}}}, }}}, []string{ - fmt.Sprintf("model/resources/object[1]/mesh/beamlattice: %v", errors.ErrMissingResource), - fmt.Sprintf("model/resources/object[1]/mesh/beamlattice: %v", errors.ErrRecursion), - fmt.Sprintf("model/resources/object[2]/mesh/beamlattice: %v", ErrLatticeInvalidMesh), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/mesh/beamlattice: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/mesh/beamlattice: %v", errors.ErrRecursion), + fmt.Sprintf("go3mf: XPath: /model/resources/object[2]/mesh/beamlattice: %v", ErrLatticeInvalidMesh), }}, {"incorrect beams", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ @@ -75,10 +75,10 @@ func TestValidate(t *testing.T) { }, }}}}}, }}}, []string{ - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beam[0]: %v", ErrLatticeSameVertex), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beam[1]: %v", ErrLatticeSameVertex), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beam[1]: %v", ErrLatticeBeamR2), - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beam[2]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beam[0]: %v", ErrLatticeSameVertex), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beam[1]: %v", ErrLatticeSameVertex), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beam[1]: %v", ErrLatticeBeamR2), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beam[2]: %v", errors.ErrIndexOutOfBounds), }}, {"incorrect beamseat", &go3mf.Model{Resources: go3mf.Resources{Objects: []*go3mf.Object{ {ID: 2, Mesh: &go3mf.Mesh{Vertices: go3mf.Vertices{Vertex: []go3mf.Point3D{{}, {}, {}}}, Any: spec.Any{&BeamLattice{ @@ -87,7 +87,7 @@ func TestValidate(t *testing.T) { }}, BeamSets: BeamSets{BeamSet: []BeamSet{{Refs: []uint32{0, 2, 3}}}}, }}}}, }}}, []string{ - fmt.Sprintf("model/resources/object[0]/mesh/beamlattice/beamset[0]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/beamlattice/beamset[0]: %v", errors.ErrIndexOutOfBounds), }}, } for _, tt := range tests { diff --git a/errors/errors.go b/errors/errors.go index 0aaac44..db38bad 100644 --- a/errors/errors.go +++ b/errors/errors.go @@ -99,16 +99,19 @@ func (e *Error) Unwrap() error { return e.Err } -func (e *Error) Error() string { - levels := make([]string, len(e.Target)+1) - levels[0] = e.Path +func (e *Error) XPath() string { + levels := make([]string, len(e.Target)) for i, l := range e.Target { - levels[len(e.Target)-i] = l.String() + levels[len(e.Target)-i-1] = l.String() } + return "/" + strings.Join(levels, "/") +} + +func (e *Error) Error() string { if e.Path == "" { - levels = levels[1:] + return fmt.Sprintf("go3mf: XPath: %s: %v", e.XPath(), e.Err) } - return fmt.Sprintf("%s: %v", strings.Join(levels, "/"), e.Err) + return fmt.Sprintf("go3mf: Path: %s XPath: %s: %v", e.Path, e.XPath(), e.Err) } func NewMissingFieldError(name string) error { diff --git a/materials/decoder_test.go b/materials/decoder_test.go index 94ce9cf..cb10917 100644 --- a/materials/decoder_test.go +++ b/materials/decoder_test.go @@ -63,14 +63,14 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("model/resources/texture2d[1]: %v", errors.NewParseAttrError("id", true)), - fmt.Sprintf("model/resources/colorgroup[2]/color[0]: %v", errors.NewParseAttrError("color", true)), - fmt.Sprintf("model/resources/texture2dgroup[3]: %v", errors.NewParseAttrError("texid", true)), - fmt.Sprintf("model/resources/texture2dgroup[3]/tex2coord[0]: %v", errors.NewParseAttrError("u", true)), - fmt.Sprintf("model/resources/texture2dgroup[3]/tex2coord[1]: %v", errors.NewParseAttrError("v", true)), - fmt.Sprintf("model/resources/compositematerials[4]: %v", errors.NewParseAttrError("matid", true)), - fmt.Sprintf("model/resources/compositematerials[4]/composite[1]: %v", errors.NewParseAttrError("values", true)), - fmt.Sprintf("model/resources/multiproperties[5]: %v", errors.NewParseAttrError("pids", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2d[1]: %v", errors.NewParseAttrError("id", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/colorgroup[2]/color[0]: %v", errors.NewParseAttrError("color", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2dgroup[3]: %v", errors.NewParseAttrError("texid", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2dgroup[3]/tex2coord[0]: %v", errors.NewParseAttrError("u", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2dgroup[3]/tex2coord[1]: %v", errors.NewParseAttrError("v", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/compositematerials[4]: %v", errors.NewParseAttrError("matid", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/compositematerials[4]/composite[1]: %v", errors.NewParseAttrError("values", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[5]: %v", errors.NewParseAttrError("pids", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/materials/validate_test.go b/materials/validate_test.go index 7d927d7..43d6fed 100644 --- a/materials/validate_test.go +++ b/materials/validate_test.go @@ -27,10 +27,10 @@ func TestValidate(t *testing.T) { &MultiProperties{ID: 2}, }}}, }}, []string{ - fmt.Sprintf("/other.model/model/resources/colorgroup[0]: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("/that.model/model/resources/multiproperties[0]: %v", &errors.MissingFieldError{Name: attrPIDs}), - fmt.Sprintf("/that.model/model/resources/multiproperties[0]: %v", ErrMultiBlend), - fmt.Sprintf("/that.model/model/resources/multiproperties[0]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/colorgroup[0]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("go3mf: Path: /that.model XPath: /model/resources/multiproperties[0]: %v", &errors.MissingFieldError{Name: attrPIDs}), + fmt.Sprintf("go3mf: Path: /that.model XPath: /model/resources/multiproperties[0]: %v", ErrMultiBlend), + fmt.Sprintf("go3mf: Path: /that.model XPath: /model/resources/multiproperties[0]: %v", errors.ErrEmptyResourceProps), }}, {"multi", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -48,15 +48,15 @@ func TestValidate(t *testing.T) { &MultiProperties{ID: 9, Multis: []Multi{{PIndices: []uint32{}}}, PIDs: []uint32{1, 3}}, }}, }, []string{ - fmt.Sprintf("model/resources/multiproperties[0]: %v", &errors.MissingFieldError{Name: attrPIDs}), - fmt.Sprintf("model/resources/multiproperties[0]: %v", ErrMultiBlend), - fmt.Sprintf("model/resources/multiproperties[0]: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("model/resources/multiproperties[1]: %v", ErrMultiRefMulti), - fmt.Sprintf("model/resources/multiproperties[1]: %v", errors.ErrMissingResource), - fmt.Sprintf("model/resources/multiproperties[6]/multi[0]: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model/resources/multiproperties[7]: %v", ErrMaterialMulti), - fmt.Sprintf("model/resources/multiproperties[7]: %v", ErrMultiColors), - fmt.Sprintf("model/resources/multiproperties[8]: %v", ErrMaterialMulti), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[0]: %v", &errors.MissingFieldError{Name: attrPIDs}), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[0]: %v", ErrMultiBlend), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[0]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[1]: %v", ErrMultiRefMulti), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[1]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[6]/multi[0]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[7]: %v", ErrMaterialMulti), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[7]: %v", ErrMultiColors), + fmt.Sprintf("go3mf: XPath: /model/resources/multiproperties[8]: %v", ErrMaterialMulti), }}, {"missingTextPart", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -64,9 +64,9 @@ func TestValidate(t *testing.T) { &Texture2D{ID: 2, ContentType: TextureTypePNG, Path: "/a.png"}, }}, }, []string{ - fmt.Sprintf("model/resources/texture2d[0]: %v", &errors.MissingFieldError{Name: attrPath}), - fmt.Sprintf("model/resources/texture2d[0]: %v", &errors.MissingFieldError{Name: attrContentType}), - fmt.Sprintf("model/resources/texture2d[1]: %v", ErrMissingTexturePart), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2d[0]: %v", &errors.MissingFieldError{Name: attrPath}), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2d[0]: %v", &errors.MissingFieldError{Name: attrContentType}), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2d[1]: %v", ErrMissingTexturePart), }}, {"textureGroup", &go3mf.Model{ Attachments: []go3mf.Attachment{{Path: "/a.png"}}, @@ -78,10 +78,10 @@ func TestValidate(t *testing.T) { &Texture2DGroup{ID: 5, TextureID: 100, Coords: []TextureCoord{{}}}, }}, }, []string{ - fmt.Sprintf("model/resources/texture2dgroup[1]: %v", &errors.MissingFieldError{Name: attrTexID}), - fmt.Sprintf("model/resources/texture2dgroup[1]: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("model/resources/texture2dgroup[3]: %v", ErrTextureReference), - fmt.Sprintf("model/resources/texture2dgroup[4]: %v", ErrTextureReference), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2dgroup[1]: %v", &errors.MissingFieldError{Name: attrTexID}), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2dgroup[1]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2dgroup[3]: %v", ErrTextureReference), + fmt.Sprintf("go3mf: XPath: /model/resources/texture2dgroup[4]: %v", ErrTextureReference), }}, {"colorGroup", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -90,8 +90,8 @@ func TestValidate(t *testing.T) { &ColorGroup{ID: 3, Colors: []color.RGBA{{R: 1}, {}}}, }}, }, []string{ - fmt.Sprintf("model/resources/colorgroup[0]: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("model/resources/colorgroup[2]/color[1]: %v", &errors.MissingFieldError{Name: attrColor}), + fmt.Sprintf("go3mf: XPath: /model/resources/colorgroup[0]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("go3mf: XPath: /model/resources/colorgroup[2]/color[1]: %v", &errors.MissingFieldError{Name: attrColor}), }}, {"composite", &go3mf.Model{ Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -105,12 +105,12 @@ func TestValidate(t *testing.T) { &CompositeMaterials{ID: 5, MaterialID: 2, Indices: []uint32{0, 1}, Composites: []Composite{{Values: []float32{1, 2}}}}, &CompositeMaterials{ID: 6, MaterialID: 100, Indices: []uint32{0, 1}, Composites: []Composite{{Values: []float32{1, 2}}}}, }}}, []string{ - fmt.Sprintf("model/resources/compositematerials[1]: %v", &errors.MissingFieldError{Name: attrMatID}), - fmt.Sprintf("model/resources/compositematerials[1]: %v", &errors.MissingFieldError{Name: attrMatIndices}), - fmt.Sprintf("model/resources/compositematerials[1]: %v", errors.ErrEmptyResourceProps), - fmt.Sprintf("model/resources/compositematerials[3]: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model/resources/compositematerials[4]: %v", ErrCompositeBase), - fmt.Sprintf("model/resources/compositematerials[5]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/compositematerials[1]: %v", &errors.MissingFieldError{Name: attrMatID}), + fmt.Sprintf("go3mf: XPath: /model/resources/compositematerials[1]: %v", &errors.MissingFieldError{Name: attrMatIndices}), + fmt.Sprintf("go3mf: XPath: /model/resources/compositematerials[1]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("go3mf: XPath: /model/resources/compositematerials[3]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("go3mf: XPath: /model/resources/compositematerials[4]: %v", ErrCompositeBase), + fmt.Sprintf("go3mf: XPath: /model/resources/compositematerials[5]: %v", errors.ErrMissingResource), }}, } for _, tt := range tests { diff --git a/production/decoder_test.go b/production/decoder_test.go index d7ea7c4..aaba7ef 100644 --- a/production/decoder_test.go +++ b/production/decoder_test.go @@ -73,10 +73,10 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("model/resources/object[1]: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), - fmt.Sprintf("model/resources/object[1]/components/component[0]: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), - fmt.Sprintf("model/build: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), - fmt.Sprintf("model/build/item[0]: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/components/component[0]: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("go3mf: XPath: /model/build: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), + fmt.Sprintf("go3mf: XPath: /model/build/item[0]: %v", &errors.ParseAttrError{Required: true, Name: "UUID"}), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/production/validate_test.go b/production/validate_test.go index 0638f53..99ea5b7 100644 --- a/production/validate_test.go +++ b/production/validate_test.go @@ -23,15 +23,15 @@ func TestValidate(t *testing.T) { want []string }{ {"buildNoUUID", &go3mf.Model{Build: go3mf.Build{}}, []string{ - fmt.Sprintf("model/build: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: XPath: /model/build: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"buildEmptyUUID", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{}}}}, []string{ - fmt.Sprintf("model/build: %v", ErrUUID), + fmt.Sprintf("go3mf: XPath: /model/build: %v", ErrUUID), }}, {"buildNonValidUUID", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{"a-b-c-d"}}}}, []string{ - fmt.Sprintf("model/build: %v", ErrUUID), + fmt.Sprintf("go3mf: XPath: /model/build: %v", ErrUUID), }}, {"extReq", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, @@ -43,7 +43,7 @@ func TestValidate(t *testing.T) { AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ {ObjectID: 1, AnyAttr: spec.AnyAttr{&ItemAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d478", Path: "/other.model"}}}, }}}, []string{ - fmt.Sprintf("/other.model/model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"items", &go3mf.Model{Build: go3mf.Build{ AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}, Items: []*go3mf.Item{ @@ -54,11 +54,11 @@ func TestValidate(t *testing.T) { }}, Childs: map[string]*go3mf.ChildModel{"/other.model": {Resources: go3mf.Resources{Objects: []*go3mf.Object{validMesh}}}}, Resources: go3mf.Resources{Objects: []*go3mf.Object{{ID: 1, Mesh: validMesh.Mesh}}}}, []string{ - fmt.Sprintf("model/build/item[1]: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("model/build/item[2]: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("model/build/item[3]: %v", ErrUUID), - fmt.Sprintf("/other.model/model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: XPath: /model/build/item[1]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: XPath: /model/build/item[2]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: XPath: /model/build/item[3]: %v", ErrUUID), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"components", &go3mf.Model{Resources: go3mf.Resources{ Objects: []*go3mf.Object{ @@ -70,10 +70,10 @@ func TestValidate(t *testing.T) { }}}, }, }, Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}}, []string{ - fmt.Sprintf("model/resources/object[0]: %v", ErrUUID), - fmt.Sprintf("model/resources/object[1]/components/component[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("model/resources/object[1]/components/component[1]: %v", ErrUUID), - fmt.Sprintf("model/resources/object[1]/components/component[2]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]: %v", ErrUUID), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/components/component[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/components/component[1]: %v", ErrUUID), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/components/component[2]: %v", &errors.MissingFieldError{Name: attrProdUUID}), }}, {"child", &go3mf.Model{Build: go3mf.Build{AnyAttr: spec.AnyAttr{&BuildAttr{UUID: "f47ac10b-58cc-0372-8567-0e02b2c3d479"}}}, Childs: map[string]*go3mf.ChildModel{ @@ -83,10 +83,10 @@ func TestValidate(t *testing.T) { {ObjectID: 1, AnyAttr: spec.AnyAttr{&ComponentAttr{Path: "/b.model"}}}, }}}, }}}}}, []string{ - fmt.Sprintf("/b.model/model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model/model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model/model/resources/object[0]/components/component[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), - fmt.Sprintf("/other.model/model/resources/object[0]/components/component[0]: %v", ErrProdRefInNonRoot), + fmt.Sprintf("go3mf: Path: /b.model XPath: /model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]/components/component[0]: %v", &errors.MissingFieldError{Name: attrProdUUID}), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]/components/component[0]: %v", ErrProdRefInNonRoot), }}, } for _, tt := range tests { diff --git a/read_test.go b/read_test.go index eed2aad..5cd2505 100644 --- a/read_test.go +++ b/read_test.go @@ -650,17 +650,17 @@ func TestNewDecoder(t *testing.T) { func TestDecoder_processRootModel_warns(t *testing.T) { spec.Register(fakeSpec.Namespace, new(qmExtension)) want := []string{ - fmt.Sprintf("model/resources/basematerials[0]/base[0]: %v", specerr.NewParseAttrError("displaycolor", true)), - fmt.Sprintf("model/resources/basematerials[1]: %v", specerr.NewParseAttrError("id", true)), - fmt.Sprintf("model/resources/object[0]/mesh/vertices/vertex[8]: %v", specerr.NewParseAttrError("x", true)), - fmt.Sprintf("model/resources/object[0]/mesh/triangles/triangle[13]: %v", specerr.NewParseAttrError("v1", true)), - fmt.Sprintf("model/resources/object[1]: %v", specerr.NewParseAttrError("pid", false)), - fmt.Sprintf("model/resources/object[1]: %v", specerr.NewParseAttrError("pindex", false)), - fmt.Sprintf("model/resources/object[1]: %v", specerr.NewParseAttrError("type", false)), - fmt.Sprintf("model/resources/object[2]/components/component[0]: %v", specerr.NewParseAttrError("transform", false)), - fmt.Sprintf("model/resources/object[2]/components/component[1]: %v", specerr.NewParseAttrError("objectid", true)), - fmt.Sprintf("model/build/item[0]: %v", specerr.NewParseAttrError("transform", false)), - fmt.Sprintf("model/build/item[3]: %v", specerr.NewParseAttrError("objectid", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/basematerials[0]/base[0]: %v", specerr.NewParseAttrError("displaycolor", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/basematerials[1]: %v", specerr.NewParseAttrError("id", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/vertices/vertex[8]: %v", specerr.NewParseAttrError("x", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh/triangles/triangle[13]: %v", specerr.NewParseAttrError("v1", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]: %v", specerr.NewParseAttrError("pid", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]: %v", specerr.NewParseAttrError("pindex", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]: %v", specerr.NewParseAttrError("type", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[2]/components/component[0]: %v", specerr.NewParseAttrError("transform", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[2]/components/component[1]: %v", specerr.NewParseAttrError("objectid", true)), + fmt.Sprintf("go3mf: XPath: /model/build/item[0]: %v", specerr.NewParseAttrError("transform", false)), + fmt.Sprintf("go3mf: XPath: /model/build/item[3]: %v", specerr.NewParseAttrError("objectid", true)), } got := new(Model) got.Extensions = append(got.Extensions, fakeSpec) diff --git a/slices/decoder_test.go b/slices/decoder_test.go index 58f764c..b03c136 100644 --- a/slices/decoder_test.go +++ b/slices/decoder_test.go @@ -95,16 +95,16 @@ func TestDecode(t *testing.T) { func TestDecode_warns(t *testing.T) { want := []string{ - fmt.Sprintf("model/resources/slicestack[0]: %v", specerr.NewParseAttrError("id", true)), - fmt.Sprintf("model/resources/slicestack[0]: %v", specerr.NewParseAttrError("zbottom", false)), - fmt.Sprintf("model/resources/slicestack[0]/slice[0]/vertices/vertex[0]: %v", specerr.NewParseAttrError("x", true)), - fmt.Sprintf("model/resources/slicestack[0]/slice[0]/vertices/vertex[1]: %v", specerr.NewParseAttrError("y", true)), - fmt.Sprintf("model/resources/slicestack[0]/slice[1]: %v", specerr.NewParseAttrError("ztop", true)), - fmt.Sprintf("model/resources/slicestack[0]/slice[1]/polygon[0]: %v", specerr.NewParseAttrError("startv", true)), - fmt.Sprintf("model/resources/slicestack[0]/slice[1]/polygon[0]/segment[1]: %v", specerr.NewParseAttrError("v2", true)), - fmt.Sprintf("model/resources/slicestack[0]/sliceref[0]: %v", specerr.NewParseAttrError("slicestackid", true)), - fmt.Sprintf("model/resources/object[0]: %v", specerr.NewParseAttrError("meshresolution", false)), - fmt.Sprintf("model/resources/object[0]: %v", specerr.NewParseAttrError("slicestackid", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]: %v", specerr.NewParseAttrError("id", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]: %v", specerr.NewParseAttrError("zbottom", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[0]/vertices/vertex[0]: %v", specerr.NewParseAttrError("x", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[0]/vertices/vertex[1]: %v", specerr.NewParseAttrError("y", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[1]: %v", specerr.NewParseAttrError("ztop", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[1]/polygon[0]: %v", specerr.NewParseAttrError("startv", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[1]/polygon[0]/segment[1]: %v", specerr.NewParseAttrError("v2", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/sliceref[0]: %v", specerr.NewParseAttrError("slicestackid", true)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]: %v", specerr.NewParseAttrError("meshresolution", false)), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]: %v", specerr.NewParseAttrError("slicestackid", true)), } got := new(go3mf.Model) got.Path = "/3D/3dmodel.model" diff --git a/slices/validate_test.go b/slices/validate_test.go index 799fcf5..17c6b99 100644 --- a/slices/validate_test.go +++ b/slices/validate_test.go @@ -33,7 +33,7 @@ func TestValidate(t *testing.T) { }}}, }}, }, []string{ - fmt.Sprintf("model/resources/object[0]: %v", errors.ErrInvalidObject), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]: %v", errors.ErrInvalidObject), }}, {"child", &go3mf.Model{Childs: map[string]*go3mf.ChildModel{ "/other.model": {Resources: go3mf.Resources{Assets: []go3mf.Asset{ @@ -43,8 +43,8 @@ func TestValidate(t *testing.T) { &SliceStack{ID: 2}, }}}, }}, []string{ - fmt.Sprintf("/other.model/model/resources/slicestack[0]: %v", ErrSlicesAndRefs), - fmt.Sprintf("/that.model/model/resources/slicestack[0]: %v", ErrSlicesAndRefs), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/slicestack[0]: %v", ErrSlicesAndRefs), + fmt.Sprintf("go3mf: Path: /that.model XPath: /model/resources/slicestack[0]: %v", ErrSlicesAndRefs), }}, {"slicestack", &go3mf.Model{Resources: go3mf.Resources{ Assets: []go3mf.Asset{&SliceStack{ @@ -60,13 +60,13 @@ func TestValidate(t *testing.T) { }, }}, }}, []string{ - fmt.Sprintf("model/resources/slicestack[0]/slice[0]: %v", &errors.MissingFieldError{Name: attrZTop}), - fmt.Sprintf("model/resources/slicestack[0]/slice[1]: %v", ErrSliceSmallTopZ), - fmt.Sprintf("model/resources/slicestack[0]/slice[1]: %v", ErrSliceInsufficientVertices), - fmt.Sprintf("model/resources/slicestack[0]/slice[1]: %v", ErrSliceInsufficientPolygons), - fmt.Sprintf("model/resources/slicestack[0]/slice[2]/polygon[0]: %v", ErrSliceInsufficientSegments), - fmt.Sprintf("model/resources/slicestack[0]/slice[3]: %v", ErrSliceNoMonotonic), - fmt.Sprintf("model/resources/slicestack[0]/slice[4]: %v", ErrSliceNoMonotonic), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[0]: %v", &errors.MissingFieldError{Name: attrZTop}), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[1]: %v", ErrSliceSmallTopZ), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[1]: %v", ErrSliceInsufficientVertices), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[1]: %v", ErrSliceInsufficientPolygons), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[2]/polygon[0]: %v", ErrSliceInsufficientSegments), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[3]: %v", ErrSliceNoMonotonic), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[0]/slice[4]: %v", ErrSliceNoMonotonic), }}, {"sliceref", &go3mf.Model{ Childs: map[string]*go3mf.ChildModel{ @@ -91,13 +91,13 @@ func TestValidate(t *testing.T) { }, }}, }}, []string{ - fmt.Sprintf("model/resources/slicestack[1]/sliceref[0]: %v", &errors.MissingFieldError{Name: attrSlicePath}), - fmt.Sprintf("model/resources/slicestack[1]/sliceref[0]: %v", &errors.MissingFieldError{Name: attrSliceRefID}), - fmt.Sprintf("model/resources/slicestack[1]/sliceref[1]: %v", ErrSliceRefSamePart), - fmt.Sprintf("model/resources/slicestack[1]/sliceref[2]: %v", errors.ErrMissingResource), - fmt.Sprintf("model/resources/slicestack[1]/sliceref[3]: %v", ErrSliceRefRef), - fmt.Sprintf("model/resources/slicestack[1]/sliceref[4]: %v", ErrNonSliceStack), - fmt.Sprintf("model/resources/slicestack[1]/sliceref[6]: %v", ErrSliceNoMonotonic), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[1]/sliceref[0]: %v", &errors.MissingFieldError{Name: attrSlicePath}), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[1]/sliceref[0]: %v", &errors.MissingFieldError{Name: attrSliceRefID}), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[1]/sliceref[1]: %v", ErrSliceRefSamePart), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[1]/sliceref[2]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[1]/sliceref[3]: %v", ErrSliceRefRef), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[1]/sliceref[4]: %v", ErrNonSliceStack), + fmt.Sprintf("go3mf: XPath: /model/resources/slicestack[1]/sliceref[6]: %v", ErrSliceNoMonotonic), }}, {"info", &go3mf.Model{Build: go3mf.Build{Items: []*go3mf.Item{ {ObjectID: 7}, @@ -153,18 +153,18 @@ func TestValidate(t *testing.T) { SliceStackID: 11, }}}, }}}, []string{ - fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("model/resources/object[0]: %v", errors.ErrMissingResource), - fmt.Sprintf("model/resources/object[1]: %v", ErrSliceInvalidTranform), - fmt.Sprintf("model/resources/object[1]: %v", ErrSliceExtRequired), - fmt.Sprintf("model/resources/object[2]: %v", &errors.MissingFieldError{Name: attrSliceRefID}), - fmt.Sprintf("model/resources/object[3]: %v", ErrNonSliceStack), - fmt.Sprintf("model/resources/object[4]: %v", ErrSliceInvalidTranform), - fmt.Sprintf("model/resources/object[4]: %v", ErrSlicePolygonNotClosed), - fmt.Sprintf("model/resources/object[5]: %v", ErrSliceInvalidTranform), - fmt.Sprintf("model/resources/object[5]: %v", ErrSlicePolygonNotClosed), - fmt.Sprintf("model/resources/object[6]/components/component[1]: %v", errors.ErrRecursion), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]: %v", ErrSliceInvalidTranform), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]: %v", ErrSliceExtRequired), + fmt.Sprintf("go3mf: XPath: /model/resources/object[2]: %v", &errors.MissingFieldError{Name: attrSliceRefID}), + fmt.Sprintf("go3mf: XPath: /model/resources/object[3]: %v", ErrNonSliceStack), + fmt.Sprintf("go3mf: XPath: /model/resources/object[4]: %v", ErrSliceInvalidTranform), + fmt.Sprintf("go3mf: XPath: /model/resources/object[4]: %v", ErrSlicePolygonNotClosed), + fmt.Sprintf("go3mf: XPath: /model/resources/object[5]: %v", ErrSliceInvalidTranform), + fmt.Sprintf("go3mf: XPath: /model/resources/object[5]: %v", ErrSlicePolygonNotClosed), + fmt.Sprintf("go3mf: XPath: /model/resources/object[6]/components/component[1]: %v", errors.ErrRecursion), }}, } for _, tt := range tests { diff --git a/validate_test.go b/validate_test.go index 44ef45e..422f60a 100644 --- a/validate_test.go +++ b/validate_test.go @@ -27,26 +27,26 @@ func TestValidate(t *testing.T) { {}, {Path: "/.png"}, {Path: "/a.png"}, {Path: "a.png"}, {Path: "/b.png"}, {Path: "/a.png"}, {Path: "/a.png", Type: RelTypePrintTicket}, {Path: "/a.png", Type: RelTypePrintTicket}, }}, []string{ - fmt.Sprintf("/3D/3dmodel.model/model/relationship[0]: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model/model/relationship[1]: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model/model/relationship[3]: %v", errors.ErrOPCPartName), - fmt.Sprintf("/3D/3dmodel.model/model/relationship[4]: %v", errors.ErrOPCRelTarget), - fmt.Sprintf("/3D/3dmodel.model/model/relationship[5]: %v", errors.ErrOPCDuplicatedRel), - fmt.Sprintf("/3D/3dmodel.model/model/relationship[6]: %v", errors.ErrOPCContentType), - fmt.Sprintf("/3D/3dmodel.model/model/relationship[7]: %v", errors.ErrOPCDuplicatedRel), - fmt.Sprintf("/3D/3dmodel.model/model/relationship[7]: %v", errors.ErrOPCContentType), - fmt.Sprintf("/3D/3dmodel.model/model/relationship[7]: %v", errors.ErrOPCDuplicatedTicket), + fmt.Sprintf("go3mf: Path: /3D/3dmodel.model XPath: /model/relationship[0]: %v", errors.ErrOPCPartName), + fmt.Sprintf("go3mf: Path: /3D/3dmodel.model XPath: /model/relationship[1]: %v", errors.ErrOPCPartName), + fmt.Sprintf("go3mf: Path: /3D/3dmodel.model XPath: /model/relationship[3]: %v", errors.ErrOPCPartName), + fmt.Sprintf("go3mf: Path: /3D/3dmodel.model XPath: /model/relationship[4]: %v", errors.ErrOPCRelTarget), + fmt.Sprintf("go3mf: Path: /3D/3dmodel.model XPath: /model/relationship[5]: %v", errors.ErrOPCDuplicatedRel), + fmt.Sprintf("go3mf: Path: /3D/3dmodel.model XPath: /model/relationship[6]: %v", errors.ErrOPCContentType), + fmt.Sprintf("go3mf: Path: /3D/3dmodel.model XPath: /model/relationship[7]: %v", errors.ErrOPCDuplicatedRel), + fmt.Sprintf("go3mf: Path: /3D/3dmodel.model XPath: /model/relationship[7]: %v", errors.ErrOPCContentType), + fmt.Sprintf("go3mf: Path: /3D/3dmodel.model XPath: /model/relationship[7]: %v", errors.ErrOPCDuplicatedTicket), }}, {"namespaces", &Model{Extensions: []Extension{{Namespace: "fake", LocalName: "f", IsRequired: true}}}, []string{ - fmt.Sprintf("model: %v", errors.ErrRequiredExt), + fmt.Sprintf("go3mf: XPath: /model: %v", errors.ErrRequiredExt), }}, {"metadata", &Model{Extensions: []Extension{{Namespace: "fake", LocalName: "f"}}, Metadata: []Metadata{ {Name: xml.Name{Space: "fake", Local: "issue"}}, {Name: xml.Name{Space: "f", Local: "issue"}}, {Name: xml.Name{Space: "fake", Local: "issue"}}, {Name: xml.Name{Local: "issue"}}, {}, }}, []string{ - fmt.Sprintf("model/metadata[1]: %v", errors.ErrMetadataNamespace), - fmt.Sprintf("model/metadata[2]: %v", errors.ErrMetadataDuplicated), - fmt.Sprintf("model/metadata[3]: %v", errors.ErrMetadataName), - fmt.Sprintf("model/metadata[4]: %v", &errors.MissingFieldError{Name: attrName}), + fmt.Sprintf("go3mf: XPath: /model/metadata[1]: %v", errors.ErrMetadataNamespace), + fmt.Sprintf("go3mf: XPath: /model/metadata[2]: %v", errors.ErrMetadataDuplicated), + fmt.Sprintf("go3mf: XPath: /model/metadata[3]: %v", errors.ErrMetadataName), + fmt.Sprintf("go3mf: XPath: /model/metadata[4]: %v", &errors.MissingFieldError{Name: attrName}), }}, {"build", &Model{Resources: Resources{Assets: []Asset{&BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}}, Objects: []*Object{ {ID: 2, Type: ObjectTypeOther, Mesh: &Mesh{Vertices: Vertices{Vertex: []Point3D{{}, {}, {}, {}}}, Triangles: Triangles{Triangle: []Triangle{ @@ -57,31 +57,31 @@ func TestValidate(t *testing.T) { {ObjectID: 100}, {ObjectID: 1, Metadata: MetadataGroup{Metadata: []Metadata{{Name: xml.Name{Local: "issue"}}}}}, }}}, []string{ - "model: Build: fake", - fmt.Sprintf("model/build/item[0]: %v", &errors.MissingFieldError{Name: attrObjectID}), - fmt.Sprintf("model/build/item[1]: %v", errors.ErrOtherItem), - fmt.Sprintf("model/build/item[2]: %v", errors.ErrMissingResource), - fmt.Sprintf("model/build/item[3]: %v", errors.ErrMissingResource), - fmt.Sprintf("model/build/item[3]/metadata[0]: %v", errors.ErrMetadataName), + "go3mf: XPath: /model: Build: fake", + fmt.Sprintf("go3mf: XPath: /model/build/item[0]: %v", &errors.MissingFieldError{Name: attrObjectID}), + fmt.Sprintf("go3mf: XPath: /model/build/item[1]: %v", errors.ErrOtherItem), + fmt.Sprintf("go3mf: XPath: /model/build/item[2]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/build/item[3]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/build/item[3]/metadata[0]: %v", errors.ErrMetadataName), }}, {"childs", &Model{Childs: map[string]*ChildModel{DefaultModelPath: {}, "/a.model": { Relationships: make([]Relationship, 1), Resources: Resources{Objects: []*Object{{}}}}}}, []string{ - fmt.Sprintf("model: %v", errors.ErrOPCDuplicatedModelName), - fmt.Sprintf("/a.model/model/relationship[0]: %v", errors.ErrOPCPartName), - fmt.Sprintf("/a.model/model/resources/object[0]: %v", errors.ErrMissingID), - fmt.Sprintf("/a.model/model/resources/object[0]: %v", errors.ErrInvalidObject), + fmt.Sprintf("go3mf: XPath: /model: %v", errors.ErrOPCDuplicatedModelName), + fmt.Sprintf("go3mf: Path: /a.model XPath: /model/relationship[0]: %v", errors.ErrOPCPartName), + fmt.Sprintf("go3mf: Path: /a.model XPath: /model/resources/object[0]: %v", errors.ErrMissingID), + fmt.Sprintf("go3mf: Path: /a.model XPath: /model/resources/object[0]: %v", errors.ErrInvalidObject), }}, {"assets", &Model{Resources: Resources{Assets: []Asset{ &BaseMaterials{Materials: []Base{{Color: color.RGBA{}}}}, &BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}}}, &BaseMaterials{ID: 1}, }}}, []string{ - fmt.Sprintf("model/resources/basematerials[0]: %v", errors.ErrMissingID), - fmt.Sprintf("model/resources/basematerials[0]/base[0]: %v", &errors.MissingFieldError{Name: attrName}), - fmt.Sprintf("model/resources/basematerials[0]/base[0]: %v", &errors.MissingFieldError{Name: attrDisplayColor}), - fmt.Sprintf("model/resources/basematerials[2]: %v", errors.ErrDuplicatedID), - fmt.Sprintf("model/resources/basematerials[2]: %v", errors.ErrEmptyResourceProps), + fmt.Sprintf("go3mf: XPath: /model/resources/basematerials[0]: %v", errors.ErrMissingID), + fmt.Sprintf("go3mf: XPath: /model/resources/basematerials[0]/base[0]: %v", &errors.MissingFieldError{Name: attrName}), + fmt.Sprintf("go3mf: XPath: /model/resources/basematerials[0]/base[0]: %v", &errors.MissingFieldError{Name: attrDisplayColor}), + fmt.Sprintf("go3mf: XPath: /model/resources/basematerials[2]: %v", errors.ErrDuplicatedID), + fmt.Sprintf("go3mf: XPath: /model/resources/basematerials[2]: %v", errors.ErrEmptyResourceProps), }}, {"objects", &Model{Resources: Resources{Assets: []Asset{ &BaseMaterials{ID: 1, Materials: []Base{{Name: "a", Color: color.RGBA{A: 1}}, {Name: "b", Color: color.RGBA{A: 1}}}}, @@ -104,29 +104,29 @@ func TestValidate(t *testing.T) { {V1: 1, V2: 2, V3: 3, PID: 100, P1: 0, P2: 0, P3: 0}, }}}}, }}}, []string{ - fmt.Sprintf("model/resources/object[0]: %v", errors.ErrMissingID), - fmt.Sprintf("model/resources/object[0]: %v", errors.ErrInvalidObject), - fmt.Sprintf("model/resources/object[1]: %v", errors.ErrDuplicatedID), - fmt.Sprintf("model/resources/object[1]: %v", &errors.MissingFieldError{Name: attrPID}), - fmt.Sprintf("model/resources/object[1]: %v", errors.ErrInvalidObject), - fmt.Sprintf("model/resources/object[1]/mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("model/resources/object[1]/mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("model/resources/object[1]/components/component[0]: %v", errors.ErrRecursion), - fmt.Sprintf("model/resources/object[3]: %v", errors.ErrComponentsPID), - fmt.Sprintf("model/resources/object[3]/components/component[0]: %v", errors.ErrRecursion), - fmt.Sprintf("model/resources/object[3]/components/component[2]: %v", &errors.MissingFieldError{Name: attrObjectID}), - fmt.Sprintf("model/resources/object[3]/components/component[3]: %v", errors.ErrMissingResource), - fmt.Sprintf("model/resources/object[3]/components/component[4]: %v", errors.ErrMissingResource), - fmt.Sprintf("model/resources/object[4]: %v", errors.ErrMissingResource), - fmt.Sprintf("model/resources/object[4]/mesh: %v", errors.ErrInsufficientVertices), - fmt.Sprintf("model/resources/object[4]/mesh: %v", errors.ErrInsufficientTriangles), - fmt.Sprintf("model/resources/object[4]/mesh/triangle[0]: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("model/resources/object[4]/mesh/triangle[1]: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("model/resources/object[4]/mesh/triangle[2]: %v", errors.ErrDuplicatedIndices), - fmt.Sprintf("model/resources/object[5]: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model/resources/object[5]/mesh/triangle[0]: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model/resources/object[5]/mesh/triangle[1]: %v", errors.ErrIndexOutOfBounds), - fmt.Sprintf("model/resources/object[5]/mesh/triangle[3]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]: %v", errors.ErrMissingID), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]: %v", errors.ErrInvalidObject), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]: %v", errors.ErrDuplicatedID), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]: %v", &errors.MissingFieldError{Name: attrPID}), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]: %v", errors.ErrInvalidObject), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("go3mf: XPath: /model/resources/object[1]/components/component[0]: %v", errors.ErrRecursion), + fmt.Sprintf("go3mf: XPath: /model/resources/object[3]: %v", errors.ErrComponentsPID), + fmt.Sprintf("go3mf: XPath: /model/resources/object[3]/components/component[0]: %v", errors.ErrRecursion), + fmt.Sprintf("go3mf: XPath: /model/resources/object[3]/components/component[2]: %v", &errors.MissingFieldError{Name: attrObjectID}), + fmt.Sprintf("go3mf: XPath: /model/resources/object[3]/components/component[3]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/object[3]/components/component[4]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/object[4]: %v", errors.ErrMissingResource), + fmt.Sprintf("go3mf: XPath: /model/resources/object[4]/mesh: %v", errors.ErrInsufficientVertices), + fmt.Sprintf("go3mf: XPath: /model/resources/object[4]/mesh: %v", errors.ErrInsufficientTriangles), + fmt.Sprintf("go3mf: XPath: /model/resources/object[4]/mesh/triangle[0]: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("go3mf: XPath: /model/resources/object[4]/mesh/triangle[1]: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("go3mf: XPath: /model/resources/object[4]/mesh/triangle[2]: %v", errors.ErrDuplicatedIndices), + fmt.Sprintf("go3mf: XPath: /model/resources/object[5]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("go3mf: XPath: /model/resources/object[5]/mesh/triangle[0]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("go3mf: XPath: /model/resources/object[5]/mesh/triangle[1]: %v", errors.ErrIndexOutOfBounds), + fmt.Sprintf("go3mf: XPath: /model/resources/object[5]/mesh/triangle[3]: %v", errors.ErrMissingResource), }}, } for _, tt := range tests { @@ -211,8 +211,8 @@ func TestModel_ValidateCoherency(t *testing.T) { }}, Childs: map[string]*ChildModel{"/other.model": {Resources: Resources{Objects: []*Object{ {Mesh: invalidMesh}, }}}}}, []string{ - fmt.Sprintf("/other.model/model/resources/object[0]/mesh: %v", errors.ErrMeshConsistency), - fmt.Sprintf("model/resources/object[0]/mesh: %v", errors.ErrMeshConsistency), + fmt.Sprintf("go3mf: Path: /other.model XPath: /model/resources/object[0]/mesh: %v", errors.ErrMeshConsistency), + fmt.Sprintf("go3mf: XPath: /model/resources/object[0]/mesh: %v", errors.ErrMeshConsistency), }}, } for _, tt := range tests {