aboutsummaryrefslogtreecommitdiff
path: root/spdx/common/identifier.go
diff options
context:
space:
mode:
Diffstat (limited to 'spdx/common/identifier.go')
-rw-r--r--spdx/common/identifier.go173
1 files changed, 173 insertions, 0 deletions
diff --git a/spdx/common/identifier.go b/spdx/common/identifier.go
new file mode 100644
index 0000000..806a815
--- /dev/null
+++ b/spdx/common/identifier.go
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
+
+package common
+
+import (
+ "encoding/json"
+ "fmt"
+ "strings"
+)
+
+const (
+ spdxRefPrefix = "SPDXRef-"
+ documentRefPrefix = "DocumentRef-"
+)
+
+// ElementID represents the identifier string portion of an SPDX element
+// identifier. DocElementID should be used for any attributes which can
+// contain identifiers defined in a different SPDX document.
+// ElementIDs should NOT contain the mandatory 'SPDXRef-' portion.
+type ElementID string
+
+// MarshalJSON returns an SPDXRef- prefixed JSON string
+func (d ElementID) MarshalJSON() ([]byte, error) {
+ return json.Marshal(prefixElementId(d))
+}
+
+// UnmarshalJSON validates SPDXRef- prefixes and removes them when processing ElementIDs
+func (d *ElementID) UnmarshalJSON(data []byte) error {
+ // SPDX identifier will simply be a string
+ idStr := string(data)
+ idStr = strings.Trim(idStr, "\"")
+
+ e, err := trimElementIdPrefix(idStr)
+ if err != nil {
+ return err
+ }
+ *d = e
+ return nil
+}
+
+// prefixElementId adds the SPDXRef- prefix to an element ID if it does not have one
+func prefixElementId(id ElementID) string {
+ val := string(id)
+ if !strings.HasPrefix(val, spdxRefPrefix) {
+ return spdxRefPrefix + val
+ }
+ return val
+}
+
+// trimElementIdPrefix removes the SPDXRef- prefix from an element ID string or returns an error if it
+// does not start with SPDXRef-
+func trimElementIdPrefix(id string) (ElementID, error) {
+ // handle SPDXRef-
+ idFields := strings.SplitN(id, spdxRefPrefix, 2)
+ if len(idFields) != 2 {
+ return "", fmt.Errorf("failed to parse SPDX identifier '%s'", id)
+ }
+
+ e := ElementID(idFields[1])
+ return e, nil
+}
+
+// DocElementID represents an SPDX element identifier that could be defined
+// in a different SPDX document, and therefore could have a "DocumentRef-"
+// portion, such as Relationships and Annotations.
+// ElementID is used for attributes in which a "DocumentRef-" portion cannot
+// appear, such as a Package or File definition (since it is necessarily
+// being defined in the present document).
+// DocumentRefID will be the empty string for elements defined in the
+// present document.
+// DocElementIDs should NOT contain the mandatory 'DocumentRef-' or
+// 'SPDXRef-' portions.
+// SpecialID is used ONLY if the DocElementID matches a defined set of
+// permitted special values for a particular field, e.g. "NONE" or
+// "NOASSERTION" for the right-hand side of Relationships. If SpecialID
+// is set, DocumentRefID and ElementRefID should be empty (and vice versa).
+type DocElementID struct {
+ DocumentRefID string
+ ElementRefID ElementID
+ SpecialID string
+}
+
+// MarshalJSON converts the receiver into a slice of bytes representing a DocElementID in string form.
+// This function is also used when marshalling to YAML
+func (d DocElementID) MarshalJSON() ([]byte, error) {
+ if d.DocumentRefID != "" && d.ElementRefID != "" {
+ idStr := prefixElementId(d.ElementRefID)
+ return json.Marshal(fmt.Sprintf("%s%s:%s", documentRefPrefix, d.DocumentRefID, idStr))
+ } else if d.ElementRefID != "" {
+ return json.Marshal(prefixElementId(d.ElementRefID))
+ } else if d.SpecialID != "" {
+ return json.Marshal(d.SpecialID)
+ }
+
+ return []byte{}, fmt.Errorf("failed to marshal empty DocElementID")
+}
+
+// UnmarshalJSON takes a SPDX Identifier string parses it into a DocElementID struct.
+// This function is also used when unmarshalling YAML
+func (d *DocElementID) UnmarshalJSON(data []byte) (err error) {
+ // SPDX identifier will simply be a string
+ idStr := string(data)
+ idStr = strings.Trim(idStr, "\"")
+
+ // handle special cases
+ if idStr == "NONE" || idStr == "NOASSERTION" {
+ d.SpecialID = idStr
+ return nil
+ }
+
+ var idFields []string
+ // handle DocumentRef- if present
+ if strings.HasPrefix(idStr, documentRefPrefix) {
+ // strip out the "DocumentRef-" so we can get the value
+ idFields = strings.SplitN(idStr, documentRefPrefix, 2)
+ idStr = idFields[1]
+
+ // an SPDXRef can appear after a DocumentRef, separated by a colon
+ idFields = strings.SplitN(idStr, ":", 2)
+ d.DocumentRefID = idFields[0]
+
+ if len(idFields) == 2 {
+ idStr = idFields[1]
+ } else {
+ return nil
+ }
+ }
+
+ d.ElementRefID, err = trimElementIdPrefix(idStr)
+ return err
+}
+
+// TODO: add equivalents for LicenseRef- identifiers
+
+// MakeDocElementID takes strings (without prefixes) for the DocumentRef-
+// and SPDXRef- identifiers, and returns a DocElementID. An empty string
+// should be used for the DocumentRef- portion if it is referring to the
+// present document.
+func MakeDocElementID(docRef string, eltRef string) DocElementID {
+ return DocElementID{
+ DocumentRefID: docRef,
+ ElementRefID: ElementID(eltRef),
+ }
+}
+
+// MakeDocElementSpecial takes a "special" string (e.g. "NONE" or
+// "NOASSERTION" for the right side of a Relationship), nd returns
+// a DocElementID with it in the SpecialID field. Other fields will
+// be empty.
+func MakeDocElementSpecial(specialID string) DocElementID {
+ return DocElementID{SpecialID: specialID}
+}
+
+// RenderElementID takes an ElementID and returns the string equivalent,
+// with the SPDXRef- prefix reinserted.
+func RenderElementID(eID ElementID) string {
+ return spdxRefPrefix + string(eID)
+}
+
+// RenderDocElementID takes a DocElementID and returns the string equivalent,
+// with the SPDXRef- prefix (and, if applicable, the DocumentRef- prefix)
+// reinserted. If a SpecialID is present, it will be rendered verbatim and
+// DocumentRefID and ElementRefID will be ignored.
+func RenderDocElementID(deID DocElementID) string {
+ if deID.SpecialID != "" {
+ return deID.SpecialID
+ }
+ prefix := ""
+ if deID.DocumentRefID != "" {
+ prefix = documentRefPrefix + deID.DocumentRefID + ":"
+ }
+ return prefix + spdxRefPrefix + string(deID.ElementRefID)
+}