8 namespace Fossology\ReportImport;
12 use EasyRdf\RdfNamespace;
18 require_once
'ReportImportData.php';
19 require_once
'ReportImportDataItem.php';
20 require_once
'ImportSource.php';
24 const TERMS =
'http://spdx.org/rdf/terms#';
25 const SPDX_URL =
'http://spdx.org/licenses/';
26 const SPDX_FILE =
'spdx:File';
37 function __construct($filename, $uri =
null)
39 $this->filename = $filename;
48 RdfNamespace::set(
'spdx', self::TERMS);
49 $this->graph = $this->loadGraph($this->filename, $this->uri);
50 $this->spdxDoc = $this->getSpdxDoc();
51 return $this->graph !==
null && $this->spdxDoc !==
null;
55 RdfNamespace::set(
'spdx', self::TERMS);
56 $this->graph = $this->loadGraph($this->filename, $this->uri);
57 $this->spdxDoc = (count($docs = $this->graph->allOfType(
"spdx:SpdxDocument"))) == 1 ? $docs[0] :
null;
58 if ($this->spdxDoc !==
null){
59 $specVersion = explode(
'-', $this->spdxDoc->getLiteral(
"spdx:specVersion"))[1];
66 private function loadGraph($filename, $uri =
null)
71 $graph->parseFile($filename,
'rdfxml', $uri);
73 $graph->parseFile($filename,
'turtle', $uri);
75 $graph->parseFile($filename,
'guess', $uri);
80 private function getSpdxDoc()
82 $docs = $this->graph->allOfType(
"spdx:SpdxDocument");
83 if (count($docs) == 1) {
86 error_log(
"ERROR: Expected exactly one SPDX document, found " . count($docs));
96 $relationship = $this->spdxDoc->getResource(
"spdx:relationship");
97 $element = $relationship->getResource(
"spdx:relatedSpdxElement");
99 $files = $element->allResources(
"spdx:hasFile");
100 if (count($files) < 1) {
101 $files = $this->getNestedFiles($element);
104 foreach ($files as $file) {
105 $fileIds[$file->getUri()] =
trim($file->getLiteral(
"spdx:fileName")->getValue());
114 private function getNestedFiles($element): array
118 $nestedRelations = $element->allResources(
"spdx:relationship");
119 foreach ($nestedRelations as $nestedRelation) {
120 $relationType = $nestedRelation->getResource(
"spdx:relationshipType");
122 $fileResource = $nestedRelation->getResource(
"spdx:relatedSpdxElement");
123 if ($fileResource !==
null) {
124 $nestedFiles[] = $fileResource;
137 $fileNode = $this->graph->resource($fileId, self::SPDX_FILE);
138 if ($fileNode->getLiteral(
"spdx:fileName") ==
null) {
144 $algoKeyPrefix = self::TERMS .
'checksumAlgorithm_';
147 $checksums = $fileNode->allResources(
"spdx:checksum");
148 foreach ($checksums as $checksum) {
149 $algorithm = $checksum->getResource(
"spdx:algorithm");
150 $value = $checksum->getLiteral(
"spdx:checksumValue");
151 if ($algorithm !=
null && $value !=
null) {
153 $algo = substr($algorithm->getUri(), strlen($algoKeyPrefix));
155 $algo = $algorithm->getUri();
157 $hashes[$algo] =
trim($value->getValue());
190 $fileNode = $this->graph->resource($fileId, self::SPDX_FILE);
192 $licenses = $fileNode->allResources(
"spdx:$kind");
195 foreach ($licenses as $license) {
196 if (!$this->isNotNoassertion($license->getUri())) {
200 foreach ($innerOutput as $innerItem) {
201 $output[] = $innerItem;
207 private function isNotNoassertion($str)
209 return !(strtolower($str) === self::TERMS .
"noassertion" ||
210 strtolower($str) ===
"http://spdx.org/licenses/noassertion");
225 if (is_string($license)) {
226 return $this->parseLicenseId($license);
227 } elseif ($license->isA(
'spdx:ExtractedLicensingInfo') ||
228 $license->isA(
'spdx:License') ||
229 $license->isA(
'spdx:ListedLicense')) {
231 } elseif ($license->isA(
'spdx:DisjunctiveLicenseSet') ||
232 $license->isA(
'spdx:ConjunctiveLicenseSet')) {
233 return $this->handleLicenseSet($license);
234 } elseif ($license->isA(
'spdx:OrLaterOperator')) {
235 return $this->handleOrLaterOperator($license);
237 if ($license instanceof Resource || $license instanceof Graph) {
238 return $this->parseLicenseId($license->getUri());
240 error_log(
"ERROR: can not handle license=[" . $license .
"] of class=[" .
241 get_class($license) .
"]");
246 private function parseLicenseId($licenseId)
248 if (!is_string($licenseId)) {
249 error_log(
"ERROR: Id not a string: " . $licenseId);
252 if (!$this->isNotNoassertion($licenseId)) {
257 $spdxId = urldecode(substr($licenseId, strlen(self::SPDX_URL)));
258 $item =
new ReportImportDataItem($spdxId);
261 error_log(
"ERROR: can not handle license with ID=" . $licenseId);
275 $licenseIdLiteral = $license->getLiteral(
"spdx:licenseId");
276 $licenseNameLiteral = $license->getLiteral(
"spdx:name");
277 if ($license->isA(
'spdx:ExtractedLicensingInfo')) {
278 $licenseTextLiteral = $license->getLiteral(
"spdx:extractedText");
280 $licenseTextLiteral = $license->getLiteral(
"spdx:licenseText");
282 if ($licenseIdLiteral !=
null && $licenseNameLiteral !=
null &&
283 $licenseTextLiteral !=
null) {
284 $seeAlsoLiteral = $license->getLiteral(
"rdfs:seeAlso");
285 $rawLicenseId = $licenseIdLiteral->getValue();
286 $licenseId = $this->stripLicenseRefPrefix($rawLicenseId);
288 if ($license->isA(
'spdx:ExtractedLicensingInfo') &&
289 (strlen($licenseId) > 33 &&
290 substr($licenseId, -33, 1) ===
"-" &&
291 ctype_alnum(substr($licenseId, -32))
293 $licenseId = substr($licenseId, 0, -33);
295 $item->setCustomText($licenseTextLiteral->getValue());
298 $item->setLicenseCandidate($licenseNameLiteral->getValue(),
299 $licenseTextLiteral->getValue(),
300 strpos($rawLicenseId, LicenseRef::SPDXREF_PREFIX),
301 ($seeAlsoLiteral !=
null) ? $seeAlsoLiteral->getValue() :
""
309 private function stripLicenseRefPrefix($licenseId)
313 return urldecode(substr($licenseId, strlen(LicenseRef::SPDXREF_PREFIX_FOSSOLOGY)));
315 return urldecode(substr($licenseId, strlen(LicenseRef::SPDXREF_PREFIX)));
317 return urldecode($licenseId);
321 private function handleLicenseSet($license)
324 $subLicenses = $license->allResources(
"spdx:member");
325 if (
sizeof($subLicenses) > 1 && $license->isA(
'spdx:DisjunctiveLicenseSet')) {
326 $output[] =
new ReportImportDataItem(
"Dual-license");
328 foreach ($subLicenses as $subLicense) {
330 $output = array_merge($output, $innerOutput);
335 private function handleOrLaterOperator($license)
338 $subLicenses = $license->allResources(
"spdx:member");
339 foreach ($subLicenses as $subLicense) {
342 foreach ($innerOutput as $innerItem) {
344 $item =
new ReportImportDataItem($innerItem->getLicenseId() .
"-or-later");
346 $innerLicenseCandidate = $innerItem->getLicenseCandidate();
347 $item->setLicenseCandidate($innerLicenseCandidate->getFullName() .
" or later",
348 $innerLicenseCandidate->getText(),
false,
349 $innerLicenseCandidate->getUrl()
372 $fileNode = $this->graph->resource($fileId, self::SPDX_FILE);
374 $copyrights = $fileNode->allLiterals(
"spdx:copyrightText");
375 if (count($copyrights) == 1 && $copyrights[0] instanceof Literal) {
376 # There should be only 1 copyright element containing 1 copyright per line
377 $copyrights = explode(
"\n",
trim($copyrights[0]->getValue()));
378 return array_map(
'trim', $copyrights);
380 # There is only 1 copyright literal or noAssertion resource
static stringEndsWith($haystack, $needle)
static stringStartsWith($haystack, $needle)
getLicenseInfoInFileForFile($propertyId)
getLicenseInfoForFile($fileId, $kind)
handleLicenseInfo($license)
getConcludedLicenseInfoForFile($propertyId)
getCopyrightTextsForFile($fileId)
char * trim(char *ptext)
Trimming whitespace.