diff options
-rw-r--r-- | v0/spdx/file.go | 34 | ||||
-rw-r--r-- | v0/tvloader/parser2v1/parse_file.go | 20 | ||||
-rw-r--r-- | v0/tvloader/parser2v1/parse_file_test.go | 202 | ||||
-rw-r--r-- | v0/tvloader/parser2v1/types.go | 1 | ||||
-rw-r--r-- | v0/tvsaver/saver2v1/save_document.go | 4 | ||||
-rw-r--r-- | v0/tvsaver/saver2v1/save_file.go | 20 | ||||
-rw-r--r-- | v0/tvsaver/saver2v1/save_file_test.go | 40 |
7 files changed, 202 insertions, 119 deletions
diff --git a/v0/spdx/file.go b/v0/spdx/file.go index a61fc4e..3732107 100644 --- a/v0/spdx/file.go +++ b/v0/spdx/file.go @@ -40,19 +40,9 @@ type File2_1 struct { FileCopyrightText string // DEPRECATED in version 2.1 of spec - // 4.9: Artifact of Project Name - // Cardinality: optional, one or many - ArtifactOfProjectName []string - - // DEPRECATED in version 2.1 of spec - // 4.10: Artifact of Project Homepage: URL or "UNKNOWN" + // 4.9-4.11: Artifact of Project variables (defined below) // Cardinality: optional, one or many - ArtifactOfProjectHomePage []string - - // DEPRECATED in version 2.1 of spec - // 4.11: Artifact of Project Uniform Resource Identifier - // Cardinality: optional, one or many - ArtifactOfProjectURI []string + ArtifactOfProjects []*ArtifactOfProject2_1 // 4.12: File Comment // Cardinality: optional, one @@ -74,3 +64,23 @@ type File2_1 struct { // Snippets contained in this File Snippets []*Snippet2_1 } + +// ArtifactOfProject2_1 is a DEPRECATED collection of data regarding +// a Package, as defined in sections 4.9-4.11 in version 2.1 of the spec. +type ArtifactOfProject2_1 struct { + + // DEPRECATED in version 2.1 of spec + // 4.9: Artifact of Project Name + // Cardinality: conditional, required if present, one per AOP + Name string + + // DEPRECATED in version 2.1 of spec + // 4.10: Artifact of Project Homepage: URL or "UNKNOWN" + // Cardinality: optional, one per AOP + HomePage string + + // DEPRECATED in version 2.1 of spec + // 4.11: Artifact of Project Uniform Resource Identifier + // Cardinality: optional, one per AOP + URI string +} diff --git a/v0/tvloader/parser2v1/parse_file.go b/v0/tvloader/parser2v1/parse_file.go index bb846fb..60d3030 100644 --- a/v0/tvloader/parser2v1/parse_file.go +++ b/v0/tvloader/parser2v1/parse_file.go @@ -9,6 +9,12 @@ import ( ) func (parser *tvParser2_1) parsePairFromFile2_1(tag string, value string) error { + // expire fileAOP for anything other than an AOPHomePage or AOPURI + // (we'll actually handle the HomePage and URI further below) + if tag != "ArtifactOfProjectHomePage" && tag != "ArtifactOfProjectURI" { + parser.fileAOP = nil + } + switch tag { // tag for creating new file section case "FileName": @@ -57,11 +63,19 @@ func (parser *tvParser2_1) parsePairFromFile2_1(tag string, value string) error case "FileCopyrightText": parser.file.FileCopyrightText = value case "ArtifactOfProjectName": - parser.file.ArtifactOfProjectName = append(parser.file.ArtifactOfProjectName, value) + parser.fileAOP = &spdx.ArtifactOfProject2_1{} + parser.file.ArtifactOfProjects = append(parser.file.ArtifactOfProjects, parser.fileAOP) + parser.fileAOP.Name = value case "ArtifactOfProjectHomePage": - parser.file.ArtifactOfProjectHomePage = append(parser.file.ArtifactOfProjectHomePage, value) + if parser.fileAOP == nil { + return fmt.Errorf("no current ArtifactOfProject found") + } + parser.fileAOP.HomePage = value case "ArtifactOfProjectURI": - parser.file.ArtifactOfProjectURI = append(parser.file.ArtifactOfProjectURI, value) + if parser.fileAOP == nil { + return fmt.Errorf("no current ArtifactOfProject found") + } + parser.fileAOP.URI = value case "FileComment": parser.file.FileComment = value case "FileNotice": diff --git a/v0/tvloader/parser2v1/parse_file_test.go b/v0/tvloader/parser2v1/parse_file_test.go index d4b24f7..087de8c 100644 --- a/v0/tvloader/parser2v1/parse_file_test.go +++ b/v0/tvloader/parser2v1/parse_file_test.go @@ -422,91 +422,90 @@ func TestParser2_1CanParseFileTags(t *testing.T) { t.Errorf("got %v for FileCopyrightText", parser.file.FileCopyrightText) } - // Artifact of Project Name - prjNames := []string{ - "project1", - "project2", - "project3", - "project4", - } - for _, pn := range prjNames { - err = parser.parsePairFromFile2_1("ArtifactOfProjectName", pn) - if err != nil { - t.Errorf("expected nil error, got %v", err) - } + // Artifact of Projects: Name, HomePage and URI + // Artifact set 1 + err = parser.parsePairFromFile2_1("ArtifactOfProjectName", "project1") + if err != nil { + t.Errorf("expected nil error, got %v", err) } - for _, pnWant := range prjNames { - flagFound := false - for _, pnCheck := range parser.file.ArtifactOfProjectName { - if pnWant == pnCheck { - flagFound = true - } - } - if flagFound == false { - t.Errorf("didn't find %s in ArtifactOfProjectName", pnWant) - } + err = parser.parsePairFromFile2_1("ArtifactOfProjectHomePage", "http://example.com/1/") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + err = parser.parsePairFromFile2_1("ArtifactOfProjectURI", "http://example.com/1/uri.whatever") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + // Artifact set 2 -- just name + err = parser.parsePairFromFile2_1("ArtifactOfProjectName", "project2") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + // Artifact set 3 -- just name and home page + err = parser.parsePairFromFile2_1("ArtifactOfProjectName", "project3") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + err = parser.parsePairFromFile2_1("ArtifactOfProjectHomePage", "http://example.com/3/") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + // Artifact set 4 -- just name and URI + err = parser.parsePairFromFile2_1("ArtifactOfProjectName", "project4") + if err != nil { + t.Errorf("expected nil error, got %v", err) } - if len(prjNames) != len(parser.file.ArtifactOfProjectName) { - t.Errorf("expected %d types in ArtifactOfProjectName, got %d", len(prjNames), - len(parser.file.ArtifactOfProjectName)) + err = parser.parsePairFromFile2_1("ArtifactOfProjectURI", "http://example.com/4/uri.whatever") + if err != nil { + t.Errorf("expected nil error, got %v", err) } - // Artifact of Project Home Page - prjPages := []string{ - "http://example.com/1/", - "http://example.com/2/", - "http://example.com/3/", + if len(parser.file.ArtifactOfProjects) != 4 { + t.Fatalf("expected len %d, got %d", 4, len(parser.file.ArtifactOfProjects)) } - for _, pg := range prjPages { - err = parser.parsePairFromFile2_1("ArtifactOfProjectHomePage", pg) - if err != nil { - t.Errorf("expected nil error, got %v", err) - } + + aop := parser.file.ArtifactOfProjects[0] + if aop.Name != "project1" { + t.Errorf("expected %v, got %v", "project1", aop.Name) } - for _, pgWant := range prjPages { - flagFound := false - for _, pgCheck := range parser.file.ArtifactOfProjectHomePage { - if pgWant == pgCheck { - flagFound = true - } - } - if flagFound == false { - t.Errorf("didn't find %s in ArtifactOfProjectHomePage", pgWant) - } + if aop.HomePage != "http://example.com/1/" { + t.Errorf("expected %v, got %v", "http://example.com/1/", aop.HomePage) } - if len(prjPages) != len(parser.file.ArtifactOfProjectHomePage) { - t.Errorf("expected %d types in ArtifactOfProjectHomePage, got %d", len(prjPages), - len(parser.file.ArtifactOfProjectHomePage)) + if aop.URI != "http://example.com/1/uri.whatever" { + t.Errorf("expected %v, got %v", "http://example.com/1/uri.whatever", aop.URI) } - // Artifact of Project URI - prjURIs := []string{ - "http://example.com/1/uri.whatever", - "http://example.com/2/uri.whatever", - "http://example.com/3/uri.whatever", - "http://example.com/4/uri.whatever", - "http://example.com/5/uri.whatever", + aop = parser.file.ArtifactOfProjects[1] + if aop.Name != "project2" { + t.Errorf("expected %v, got %v", "project2", aop.Name) } - for _, pu := range prjURIs { - err = parser.parsePairFromFile2_1("ArtifactOfProjectURI", pu) - if err != nil { - t.Errorf("expected nil error, got %v", err) - } + if aop.HomePage != "" { + t.Errorf("expected %v, got %v", "", aop.HomePage) } - for _, puWant := range prjURIs { - flagFound := false - for _, puCheck := range parser.file.ArtifactOfProjectURI { - if puWant == puCheck { - flagFound = true - } - } - if flagFound == false { - t.Errorf("didn't find %s in ArtifactOfProjectURI", puWant) - } + if aop.URI != "" { + t.Errorf("expected %v, got %v", "", aop.URI) + } + + aop = parser.file.ArtifactOfProjects[2] + if aop.Name != "project3" { + t.Errorf("expected %v, got %v", "project3", aop.Name) + } + if aop.HomePage != "http://example.com/3/" { + t.Errorf("expected %v, got %v", "http://example.com/3/", aop.HomePage) } - if len(prjURIs) != len(parser.file.ArtifactOfProjectURI) { - t.Errorf("expected %d types in ArtifactOfProjectURI, got %d", len(prjURIs), - len(parser.file.ArtifactOfProjectURI)) + if aop.URI != "" { + t.Errorf("expected %v, got %v", "", aop.URI) + } + + aop = parser.file.ArtifactOfProjects[3] + if aop.Name != "project4" { + t.Errorf("expected %v, got %v", "project4", aop.Name) + } + if aop.HomePage != "" { + t.Errorf("expected %v, got %v", "", aop.HomePage) + } + if aop.URI != "http://example.com/4/uri.whatever" { + t.Errorf("expected %v, got %v", "http://example.com/4/uri.whatever", aop.URI) } // File Comment @@ -642,3 +641,60 @@ func TestParser2_1FileUnknownTagFails(t *testing.T) { t.Errorf("expected error from parsing unknown tag") } } + +func TestFileAOPPointerChangesAfterTags(t *testing.T) { + parser := tvParser2_1{ + doc: &spdx.Document2_1{}, + st: psFile2_1, + pkg: &spdx.Package2_1{PackageName: "test"}, + file: &spdx.File2_1{FileName: "f1.txt"}, + } + parser.doc.Packages = append(parser.doc.Packages, parser.pkg) + parser.pkg.Files = append(parser.pkg.Files, parser.file) + + err := parser.parsePairFromFile2_1("ArtifactOfProjectName", "project1") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + if parser.fileAOP == nil { + t.Errorf("expected non-nil AOP pointer, got nil") + } + curPtr := parser.fileAOP + + // now, a home page; pointer should stay + err = parser.parsePairFromFile2_1("ArtifactOfProjectHomePage", "http://example.com/1/") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + if parser.fileAOP != curPtr { + t.Errorf("expected no change in AOP pointer, was %v, got %v", curPtr, parser.fileAOP) + } + + // a URI; pointer should stay + err = parser.parsePairFromFile2_1("ArtifactOfProjectURI", "http://example.com/1/uri.whatever") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + if parser.fileAOP != curPtr { + t.Errorf("expected no change in AOP pointer, was %v, got %v", curPtr, parser.fileAOP) + } + + // now, another artifact name; pointer should change but be non-nil + // now, a home page; pointer should stay + err = parser.parsePairFromFile2_1("ArtifactOfProjectName", "project2") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + if parser.fileAOP == curPtr { + t.Errorf("expected change in AOP pointer, got no change") + } + + // finally, an unrelated tag; pointer should go away + err = parser.parsePairFromFile2_1("FileComment", "whatever") + if err != nil { + t.Errorf("expected nil error, got %v", err) + } + if parser.fileAOP != nil { + t.Errorf("expected nil AOP pointer, got %v", parser.fileAOP) + } +} diff --git a/v0/tvloader/parser2v1/types.go b/v0/tvloader/parser2v1/types.go index 9d0f14a..779cf96 100644 --- a/v0/tvloader/parser2v1/types.go +++ b/v0/tvloader/parser2v1/types.go @@ -17,6 +17,7 @@ type tvParser2_1 struct { pkg *spdx.Package2_1 pkgExtRef *spdx.PackageExternalReference2_1 file *spdx.File2_1 + fileAOP *spdx.ArtifactOfProject2_1 snippet *spdx.Snippet2_1 otherLic *spdx.OtherLicense2_1 rln *spdx.Relationship2_1 diff --git a/v0/tvsaver/saver2v1/save_document.go b/v0/tvsaver/saver2v1/save_document.go index 47b6b15..41e5558 100644 --- a/v0/tvsaver/saver2v1/save_document.go +++ b/v0/tvsaver/saver2v1/save_document.go @@ -11,6 +11,10 @@ import ( "github.com/swinslow/spdx-go/v0/spdx" ) +// RenderDocument2_1 is the main entry point to take an SPDX in-memory +// Document (version 2.1), and render it to the received io.Writer. +// It is only exported in order to be available to the tvsaver package, +// and typically does not need to be called by client code. func RenderDocument2_1(doc *spdx.Document2_1, w io.Writer) error { if doc.CreationInfo == nil { return fmt.Errorf("Document had nil CreationInfo section") diff --git a/v0/tvsaver/saver2v1/save_file.go b/v0/tvsaver/saver2v1/save_file.go index a82770a..4094432 100644 --- a/v0/tvsaver/saver2v1/save_file.go +++ b/v0/tvsaver/saver2v1/save_file.go @@ -40,18 +40,14 @@ func renderFile2_1(f *spdx.File2_1, w io.Writer) error { if f.FileCopyrightText != "" { fmt.Fprintf(w, "FileCopyrightText: %s\n", f.FileCopyrightText) } - // FIXME THIS IS NOT COMPLIANT WITH SPEC - // needs to be restructured so that ArtifactOfProjectHomePage and - // ArtifactOfProjectUI are associated with a particular - // ArtifactOfProjectName, and are rendered interleaved with it - for _, s := range f.ArtifactOfProjectName { - fmt.Fprintf(w, "ArtifactOfProjectName: %s\n", s) - } - for _, s := range f.ArtifactOfProjectHomePage { - fmt.Fprintf(w, "ArtifactOfProjectHomePage: %s\n", s) - } - for _, s := range f.ArtifactOfProjectURI { - fmt.Fprintf(w, "ArtifactOfProjectURI: %s\n", s) + for _, aop := range f.ArtifactOfProjects { + fmt.Fprintf(w, "ArtifactOfProjectName: %s\n", aop.Name) + if aop.HomePage != "" { + fmt.Fprintf(w, "ArtifactOfProjectHomePage: %s\n", aop.HomePage) + } + if aop.URI != "" { + fmt.Fprintf(w, "ArtifactOfProjectURI: %s\n", aop.URI) + } } if f.FileComment != "" { fmt.Fprintf(w, "FileComment: %s\n", f.FileComment) diff --git a/v0/tvsaver/saver2v1/save_file_test.go b/v0/tvsaver/saver2v1/save_file_test.go index 354a729..2386b87 100644 --- a/v0/tvsaver/saver2v1/save_file_test.go +++ b/v0/tvsaver/saver2v1/save_file_test.go @@ -28,20 +28,23 @@ func TestSaver2_1FileSavesText(t *testing.T) { }, LicenseComments: "this is a license comment(s)", FileCopyrightText: "Copyright (c) Jane Doe", - ArtifactOfProjectName: []string{ - "project1", - "project2", - "project3", - "project4", - }, - ArtifactOfProjectHomePage: []string{ - "http://example.com/1/", - "http://example.com/2/", - "http://example.com/3/", - }, - ArtifactOfProjectURI: []string{ - "http://example.com/1/uri.whatever", - "http://example.com/2/uri.whatever", + ArtifactOfProjects: []*spdx.ArtifactOfProject2_1{ + &spdx.ArtifactOfProject2_1{ + Name: "project1", + HomePage: "http://example.com/1/", + URI: "http://example.com/1/uri.whatever", + }, + &spdx.ArtifactOfProject2_1{ + Name: "project2", + }, + &spdx.ArtifactOfProject2_1{ + Name: "project3", + HomePage: "http://example.com/3/", + }, + &spdx.ArtifactOfProject2_1{ + Name: "project4", + URI: "http://example.com/4/uri.whatever", + }, }, FileComment: "this is a file comment", FileNotice: "This file may be used under either Apache-2.0 or Apache-1.1.", @@ -69,14 +72,13 @@ LicenseInfoInFile: Apache-1.1 LicenseComments: this is a license comment(s) FileCopyrightText: Copyright (c) Jane Doe ArtifactOfProjectName: project1 +ArtifactOfProjectHomePage: http://example.com/1/ +ArtifactOfProjectURI: http://example.com/1/uri.whatever ArtifactOfProjectName: project2 ArtifactOfProjectName: project3 -ArtifactOfProjectName: project4 -ArtifactOfProjectHomePage: http://example.com/1/ -ArtifactOfProjectHomePage: http://example.com/2/ ArtifactOfProjectHomePage: http://example.com/3/ -ArtifactOfProjectURI: http://example.com/1/uri.whatever -ArtifactOfProjectURI: http://example.com/2/uri.whatever +ArtifactOfProjectName: project4 +ArtifactOfProjectURI: http://example.com/4/uri.whatever FileComment: this is a file comment FileNotice: This file may be used under either Apache-2.0 or Apache-1.1. FileContributor: John Doe jdoe@example.com |