1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215
|
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
package reader
import (
"fmt"
"strings"
gordfParser "github.com/spdx/gordf/rdfloader/parser"
"github.com/spdx/tools-golang/spdx/v2/common"
"github.com/spdx/tools-golang/spdx/v2/v2_2"
)
// returns a file instance and the error if any encountered.
func (parser *rdfParser2_2) getFileFromNode(fileNode *gordfParser.Node) (file *v2_2.File, err error) {
file = &v2_2.File{}
currState := parser.cache[fileNode.ID]
if currState == nil {
// this is the first time we are seeing this node.
parser.cache[fileNode.ID] = &nodeState{
object: file,
Color: WHITE,
}
} else if currState.Color == GREY {
// we have already started parsing this file node and we needn't parse it again.
return currState.object.(*v2_2.File), nil
}
// setting color to grey to indicate that we've started parsing this node.
parser.cache[fileNode.ID].Color = GREY
// setting color to black just before function returns to the caller to
// indicate that parsing current node is complete.
defer func() { parser.cache[fileNode.ID].Color = BLACK }()
err = setFileIdentifier(fileNode.ID, file) // 4.2
if err != nil {
return nil, err
}
if existingFile := parser.files[file.FileSPDXIdentifier]; existingFile != nil {
file = existingFile
}
for _, subTriple := range parser.nodeToTriples(fileNode) {
switch subTriple.Predicate.ID {
case SPDX_FILE_NAME: // 4.1
// cardinality: exactly 1
file.FileName = subTriple.Object.ID
case SPDX_NAME:
// cardinality: exactly 1
// TODO: check where it will be set in the golang-tools spdx-data-model
case RDF_TYPE:
// cardinality: exactly 1
case SPDX_FILE_TYPE: // 4.3
// cardinality: min 0
fileType := ""
fileType, err = parser.getFileTypeFromUri(subTriple.Object.ID)
file.FileTypes = append(file.FileTypes, fileType)
case SPDX_CHECKSUM: // 4.4
// cardinality: min 1
err = parser.setFileChecksumFromNode(file, subTriple.Object)
case SPDX_LICENSE_CONCLUDED: // 4.5
// cardinality: (exactly 1 anyLicenseInfo) or (None) or (Noassertion)
anyLicense, err := parser.getAnyLicenseFromNode(subTriple.Object)
if err != nil {
return nil, fmt.Errorf("error parsing licenseConcluded: %v", err)
}
file.LicenseConcluded = anyLicense.ToLicenseString()
case SPDX_LICENSE_INFO_IN_FILE: // 4.6
// cardinality: min 1
lic, err := parser.getAnyLicenseFromNode(subTriple.Object)
if err != nil {
return nil, fmt.Errorf("error parsing licenseInfoInFile: %v", err)
}
file.LicenseInfoInFiles = append(file.LicenseInfoInFiles, lic.ToLicenseString())
case SPDX_LICENSE_COMMENTS: // 4.7
// cardinality: max 1
file.LicenseComments = subTriple.Object.ID
// TODO: allow copyright text to be of type NOASSERTION
case SPDX_COPYRIGHT_TEXT: // 4.8
// cardinality: exactly 1
file.FileCopyrightText = subTriple.Object.ID
case SPDX_LICENSE_INFO_FROM_FILES:
// TODO: implement it. It is not defined in the tools-golang model.
// deprecated artifactOf (see sections 4.9, 4.10, 4.11)
case SPDX_ARTIFACT_OF:
// cardinality: min 0
var artifactOf *v2_2.ArtifactOfProject
artifactOf, err = parser.getArtifactFromNode(subTriple.Object)
file.ArtifactOfProjects = append(file.ArtifactOfProjects, artifactOf)
case RDFS_COMMENT: // 4.12
// cardinality: max 1
file.FileComment = subTriple.Object.ID
case SPDX_NOTICE_TEXT: // 4.13
// cardinality: max 1
file.FileNotice = getNoticeTextFromNode(subTriple.Object)
case SPDX_FILE_CONTRIBUTOR: // 4.14
// cardinality: min 0
file.FileContributors = append(file.FileContributors, subTriple.Object.ID)
case SPDX_FILE_DEPENDENCY:
// cardinality: min 0
newFile, err := parser.getFileFromNode(subTriple.Object)
if err != nil {
return nil, fmt.Errorf("error setting a file dependency in a file: %v", err)
}
file.FileDependencies = append(file.FileDependencies, string(newFile.FileSPDXIdentifier))
case SPDX_ATTRIBUTION_TEXT:
// cardinality: min 0
file.FileAttributionTexts = append(file.FileAttributionTexts, subTriple.Object.ID)
case SPDX_ANNOTATION:
// cardinality: min 0
err = parser.parseAnnotationFromNode(subTriple.Object)
case SPDX_RELATIONSHIP:
// cardinality: min 0
err = parser.parseRelationship(subTriple)
default:
return nil, fmt.Errorf("unknown triple predicate id %s", subTriple.Predicate.ID)
}
if err != nil {
return nil, err
}
}
parser.files[file.FileSPDXIdentifier] = file
return file, nil
}
func (parser *rdfParser2_2) setFileChecksumFromNode(file *v2_2.File, checksumNode *gordfParser.Node) error {
checksumAlgorithm, checksumValue, err := parser.getChecksumFromNode(checksumNode)
if err != nil {
return fmt.Errorf("error parsing checksumNode of a file: %v", err)
}
if file.Checksums == nil {
file.Checksums = []common.Checksum{}
}
switch checksumAlgorithm {
case common.SHA1,
common.SHA224,
common.SHA256,
common.SHA384,
common.SHA512,
common.MD2,
common.MD4,
common.MD5,
common.MD6:
file.Checksums = append(file.Checksums, common.Checksum{Algorithm: checksumAlgorithm, Value: checksumValue})
case "":
return fmt.Errorf("empty checksum algorithm and value")
default:
return fmt.Errorf("unknown checksumAlgorithm %s for a file", checksumAlgorithm)
}
return nil
}
func (parser *rdfParser2_2) getArtifactFromNode(node *gordfParser.Node) (*v2_2.ArtifactOfProject, error) {
artifactOf := &v2_2.ArtifactOfProject{}
// setting artifactOfProjectURI attribute (which is optional)
if node.NodeType == gordfParser.IRI {
artifactOf.URI = node.ID
}
// parsing rest triples and attributes of the artifact.
for _, triple := range parser.nodeToTriples(node) {
switch triple.Predicate.ID {
case RDF_TYPE:
case DOAP_HOMEPAGE:
artifactOf.HomePage = triple.Object.ID
case DOAP_NAME:
artifactOf.Name = triple.Object.ID
default:
return nil, fmt.Errorf("error parsing artifactOf predicate %s", triple.Predicate.ID)
}
}
return artifactOf, nil
}
// TODO: check if the filetype is valid.
func (parser *rdfParser2_2) getFileTypeFromUri(uri string) (string, error) {
// fileType is given as a uri. for example: http://spdx.org/rdf/terms#fileType_text
lastPart := getLastPartOfURI(uri)
if !strings.HasPrefix(lastPart, "fileType_") {
return "", fmt.Errorf("fileType Uri must begin with fileTYpe_. found: %s", lastPart)
}
return strings.TrimPrefix(lastPart, "fileType_"), nil
}
// populates parser.doc.Files by a list of files which are not
// associated with a package by the hasFile attribute
// assumes: all the packages are already parsed.
func (parser *rdfParser2_2) setUnpackagedFiles() {
for fileID := range parser.files {
if !parser.assocWithPackage[fileID] {
parser.doc.Files = append(parser.doc.Files, parser.files[fileID])
}
}
}
func setFileIdentifier(idURI string, file *v2_2.File) (err error) {
idURI = strings.TrimSpace(idURI)
uriFragment := getLastPartOfURI(idURI)
file.FileSPDXIdentifier, err = ExtractElementID(uriFragment)
if err != nil {
return fmt.Errorf("error setting file identifier: %s", err)
}
return nil
}
func getNoticeTextFromNode(node *gordfParser.Node) string {
switch node.ID {
case SPDX_NOASSERTION_CAPS, SPDX_NOASSERTION_SMALL:
return "NOASSERTION"
default:
return node.ID
}
}
|