xref: /aosp_15_r20/external/spdx-tools/tvloader/parser2v1/parse_creation_info.go (revision ba677afa8f67bb56cbc794f4d0e378e0da058e16)
1*ba677afaSXin Li// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2*ba677afaSXin Li
3*ba677afaSXin Lipackage parser2v1
4*ba677afaSXin Li
5*ba677afaSXin Liimport (
6*ba677afaSXin Li	"fmt"
7*ba677afaSXin Li	"strings"
8*ba677afaSXin Li
9*ba677afaSXin Li	"github.com/spdx/tools-golang/spdx/common"
10*ba677afaSXin Li	"github.com/spdx/tools-golang/spdx/v2_1"
11*ba677afaSXin Li)
12*ba677afaSXin Li
13*ba677afaSXin Lifunc (parser *tvParser2_1) parsePairFromCreationInfo2_1(tag string, value string) error {
14*ba677afaSXin Li	// fail if not in Creation Info parser state
15*ba677afaSXin Li	if parser.st != psCreationInfo2_1 {
16*ba677afaSXin Li		return fmt.Errorf("got invalid state %v in parsePairFromCreationInfo2_1", parser.st)
17*ba677afaSXin Li	}
18*ba677afaSXin Li
19*ba677afaSXin Li	// create an SPDX Creation Info data struct if we don't have one already
20*ba677afaSXin Li	if parser.doc.CreationInfo == nil {
21*ba677afaSXin Li		parser.doc.CreationInfo = &v2_1.CreationInfo{}
22*ba677afaSXin Li	}
23*ba677afaSXin Li
24*ba677afaSXin Li	ci := parser.doc.CreationInfo
25*ba677afaSXin Li	switch tag {
26*ba677afaSXin Li	case "LicenseListVersion":
27*ba677afaSXin Li		ci.LicenseListVersion = value
28*ba677afaSXin Li	case "Creator":
29*ba677afaSXin Li		subkey, subvalue, err := extractSubs(value)
30*ba677afaSXin Li		if err != nil {
31*ba677afaSXin Li			return err
32*ba677afaSXin Li		}
33*ba677afaSXin Li
34*ba677afaSXin Li		creator := common.Creator{Creator: subvalue}
35*ba677afaSXin Li		switch subkey {
36*ba677afaSXin Li		case "Person", "Organization", "Tool":
37*ba677afaSXin Li			creator.CreatorType = subkey
38*ba677afaSXin Li		default:
39*ba677afaSXin Li			return fmt.Errorf("unrecognized Creator type %v", subkey)
40*ba677afaSXin Li		}
41*ba677afaSXin Li
42*ba677afaSXin Li		ci.Creators = append(ci.Creators, creator)
43*ba677afaSXin Li	case "Created":
44*ba677afaSXin Li		ci.Created = value
45*ba677afaSXin Li	case "CreatorComment":
46*ba677afaSXin Li		ci.CreatorComment = value
47*ba677afaSXin Li
48*ba677afaSXin Li	// tag for going on to package section
49*ba677afaSXin Li	case "PackageName":
50*ba677afaSXin Li		// error if last file does not have an identifier
51*ba677afaSXin Li		// this may be a null case: can we ever have a "last file" in
52*ba677afaSXin Li		// the "creation info" state? should go on to "file" state
53*ba677afaSXin Li		// even when parsing unpackaged files.
54*ba677afaSXin Li		if parser.file != nil && parser.file.FileSPDXIdentifier == nullSpdxElementId2_1 {
55*ba677afaSXin Li			return fmt.Errorf("file with FileName %s does not have SPDX identifier", parser.file.FileName)
56*ba677afaSXin Li		}
57*ba677afaSXin Li		parser.st = psPackage2_1
58*ba677afaSXin Li		parser.pkg = &v2_1.Package{
59*ba677afaSXin Li			FilesAnalyzed:             true,
60*ba677afaSXin Li			IsFilesAnalyzedTagPresent: false,
61*ba677afaSXin Li		}
62*ba677afaSXin Li		return parser.parsePairFromPackage2_1(tag, value)
63*ba677afaSXin Li	// tag for going on to _unpackaged_ file section
64*ba677afaSXin Li	case "FileName":
65*ba677afaSXin Li		// leave pkg as nil, so that packages will be placed in Files
66*ba677afaSXin Li		parser.st = psFile2_1
67*ba677afaSXin Li		parser.pkg = nil
68*ba677afaSXin Li		return parser.parsePairFromFile2_1(tag, value)
69*ba677afaSXin Li	// tag for going on to other license section
70*ba677afaSXin Li	case "LicenseID":
71*ba677afaSXin Li		parser.st = psOtherLicense2_1
72*ba677afaSXin Li		return parser.parsePairFromOtherLicense2_1(tag, value)
73*ba677afaSXin Li	// tag for going on to review section (DEPRECATED)
74*ba677afaSXin Li	case "Reviewer":
75*ba677afaSXin Li		parser.st = psReview2_1
76*ba677afaSXin Li		return parser.parsePairFromReview2_1(tag, value)
77*ba677afaSXin Li	// for relationship tags, pass along but don't change state
78*ba677afaSXin Li	case "Relationship":
79*ba677afaSXin Li		parser.rln = &v2_1.Relationship{}
80*ba677afaSXin Li		parser.doc.Relationships = append(parser.doc.Relationships, parser.rln)
81*ba677afaSXin Li		return parser.parsePairForRelationship2_1(tag, value)
82*ba677afaSXin Li	case "RelationshipComment":
83*ba677afaSXin Li		return parser.parsePairForRelationship2_1(tag, value)
84*ba677afaSXin Li	// for annotation tags, pass along but don't change state
85*ba677afaSXin Li	case "Annotator":
86*ba677afaSXin Li		parser.ann = &v2_1.Annotation{}
87*ba677afaSXin Li		parser.doc.Annotations = append(parser.doc.Annotations, parser.ann)
88*ba677afaSXin Li		return parser.parsePairForAnnotation2_1(tag, value)
89*ba677afaSXin Li	case "AnnotationDate":
90*ba677afaSXin Li		return parser.parsePairForAnnotation2_1(tag, value)
91*ba677afaSXin Li	case "AnnotationType":
92*ba677afaSXin Li		return parser.parsePairForAnnotation2_1(tag, value)
93*ba677afaSXin Li	case "SPDXREF":
94*ba677afaSXin Li		return parser.parsePairForAnnotation2_1(tag, value)
95*ba677afaSXin Li	case "AnnotationComment":
96*ba677afaSXin Li		return parser.parsePairForAnnotation2_1(tag, value)
97*ba677afaSXin Li	default:
98*ba677afaSXin Li		return fmt.Errorf("received unknown tag %v in CreationInfo section", tag)
99*ba677afaSXin Li	}
100*ba677afaSXin Li
101*ba677afaSXin Li	return nil
102*ba677afaSXin Li}
103*ba677afaSXin Li
104*ba677afaSXin Li// ===== Helper functions =====
105*ba677afaSXin Li
106*ba677afaSXin Lifunc extractExternalDocumentReference(value string) (string, string, string, string, error) {
107*ba677afaSXin Li	sp := strings.Split(value, " ")
108*ba677afaSXin Li	// remove any that are just whitespace
109*ba677afaSXin Li	keepSp := []string{}
110*ba677afaSXin Li	for _, s := range sp {
111*ba677afaSXin Li		ss := strings.TrimSpace(s)
112*ba677afaSXin Li		if ss != "" {
113*ba677afaSXin Li			keepSp = append(keepSp, ss)
114*ba677afaSXin Li		}
115*ba677afaSXin Li	}
116*ba677afaSXin Li
117*ba677afaSXin Li	var documentRefID, uri, alg, checksum string
118*ba677afaSXin Li
119*ba677afaSXin Li	// now, should have 4 items (or 3, if Alg and Checksum were joined)
120*ba677afaSXin Li	// and should be able to map them
121*ba677afaSXin Li	if len(keepSp) == 4 {
122*ba677afaSXin Li		documentRefID = keepSp[0]
123*ba677afaSXin Li		uri = keepSp[1]
124*ba677afaSXin Li		alg = keepSp[2]
125*ba677afaSXin Li		// check that colon is present for alg, and remove it
126*ba677afaSXin Li		if !strings.HasSuffix(alg, ":") {
127*ba677afaSXin Li			return "", "", "", "", fmt.Errorf("algorithm does not end with colon")
128*ba677afaSXin Li		}
129*ba677afaSXin Li		alg = strings.TrimSuffix(alg, ":")
130*ba677afaSXin Li		checksum = keepSp[3]
131*ba677afaSXin Li	} else if len(keepSp) == 3 {
132*ba677afaSXin Li		documentRefID = keepSp[0]
133*ba677afaSXin Li		uri = keepSp[1]
134*ba677afaSXin Li		// split on colon into alg and checksum
135*ba677afaSXin Li		parts := strings.SplitN(keepSp[2], ":", 2)
136*ba677afaSXin Li		if len(parts) != 2 {
137*ba677afaSXin Li			return "", "", "", "", fmt.Errorf("missing colon separator between algorithm and checksum")
138*ba677afaSXin Li		}
139*ba677afaSXin Li		alg = parts[0]
140*ba677afaSXin Li		checksum = parts[1]
141*ba677afaSXin Li	} else {
142*ba677afaSXin Li		return "", "", "", "", fmt.Errorf("expected 4 elements, got %d", len(keepSp))
143*ba677afaSXin Li	}
144*ba677afaSXin Li
145*ba677afaSXin Li	// additionally, we should be able to parse the first element as a
146*ba677afaSXin Li	// DocumentRef- ID string, and we should remove that prefix
147*ba677afaSXin Li	if !strings.HasPrefix(documentRefID, "DocumentRef-") {
148*ba677afaSXin Li		return "", "", "", "", fmt.Errorf("expected first element to have DocumentRef- prefix")
149*ba677afaSXin Li	}
150*ba677afaSXin Li	documentRefID = strings.TrimPrefix(documentRefID, "DocumentRef-")
151*ba677afaSXin Li	if documentRefID == "" {
152*ba677afaSXin Li		return "", "", "", "", fmt.Errorf("document identifier has nothing after prefix")
153*ba677afaSXin Li	}
154*ba677afaSXin Li
155*ba677afaSXin Li	return documentRefID, uri, alg, checksum, nil
156*ba677afaSXin Li}
157