blob: 932a75afec9dd6d7b6eeef4592464fbc9b1ff94b [file] [log] [blame]
<?php
/*******************************************************************************
* Copyright (c) 2021 Eclipse Foundation and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* SPDX-License-Identifier: EPL-2.0
********************************************************************************/
require_once dirname(__FILE__) . '/Project.class.php';
require_once dirname(__FILE__) . '/common.php';
class ProjectContentIdMapper {
/**
* This function answers the id of the Eclipse project that produces the bits
* with the provided ClearlyDefined coordinates.
*/
static function getEclipseProjectFor($id) {
$parts = explode('/', $id);
if (count($parts) != 5) return null;
list($type, $source, $namespace, $name, $revision) = $parts;
$namespace = preg_replace('/^p2\.eclipse\.(plugin|feature)$/', 'p2.eclipse-$1', $namespace);
if ($type == 'p2') {
// Normalize the "p2" package namespace.
$name = preg_replace('/\.source$/','',$name);
}
if ($project = self::getProjectFromHints($type, $source, $namespace, $name, $revision)) {
return $project;
}
if ($project = self::getProjectByNamespaceLookup($type, $source, $namespace, $name, $revision)) {
return $project;
}
if ($project = self::getProjectByMakingAReallyGoodGuess($type, $source, $namespace, $name, $revision)) {
return $project;
}
return null;
}
private static function getProjectFromHints($type, $source, $namespace, $name, $revision) {
$sql = "
select
id, version
from ProjectMappingHint
where ':type:' like type
and ':source:' like source
and ':namespace:' like namespace
and ':name:' like name
";
$args = array(
':type:' => $type,
':source:' => $source,
':namespace:' => $namespace,
':name:' => $name
);
$ids = array();
query('dashboard', $sql, $args, function($row) use (&$ids, &$revision) {
$version = $row['version'];
if ($version == '%') {
$ids[] = $row['id'];
} else {
$matches = null;
if (preg_match('/^(?<version>.*)\+$/', $version, $matches)) {
if (compareSemanticVersion($matches['version'], $revision) <= 0)
$ids[] = $row['id'];
} elseif (compareSemanticVersion($version, $revision) == 0) $ids[] = $row['id'];
}
});
return $ids ? $ids[0] : null;
}
/**
* Identify the project that produces an artifact with a specific identifier.
*
* This uses the ProjectNamespaces table which is generated by the POMVisitor
* class as it walks through project repositories. That visitor identifies build
* scripts and pulls namespace and name information out of them (e.g., when it
* encounters a Maven pom.xml file, it pulls the groupid and artifactid as a
* namespace and name associated with the corresponding project.
*
* There's a wrinkle. Some projects include third-party libraries in their
* repositories in source form, and sometimes those libraries include build scripts
* which the script has difficultly distinguishing from project build scripts.
*
* While we try to improve the overall quality of the scripts that harvest the
* information, we tweak our queries to be conservative when deciding whether or
* not a namespace and name is valid.
*/
private static function getProjectByNamespaceLookup($type, $source, $namespace, $name, $revision) {
$project = null;
/*
* There are some inefficiencies in this query that I'm leaving in favour
* of legibility. Specifically, we can probably do better with the redundancy
* in where clause.
*
* The substring_index bits are extracting the project's short name from the
* project id. The gist is that--at least temporarily--we only consider a
* namespace to be a project name space if it includes the actual project's
* name. This likely excludes a number of completely reasonable project
* namespaces...
*/
$sql = "
select distinct /* ProjectContentIdMapper::getProjectByNamespaceLookup */
p.id,
p.license
from Project as p
join ProjectNamespaces as pn on p.id=pn.project
where
(pn.namespace=':namespace:' and pn.name=':name:'
and pn.namespace in ('p2.eclipse-plugin', 'p2.eclipse-feature') and pn.name like 'org.eclipse.%')
or
(pn.namespace=':namespace:' and pn.name=':name:'
and (
pn.namespace like 'org.eclipse.%'
or pn.namespace like '@eclipse%'
or pn.namespace like 'org.locationtech.%'
or pn.namespace like 'org.polarsys.%'
or pn.namespace like 'org.osgi.%'
or pn.namespace like 'org.glassfish.%'
or pn.namespace like 'jakarta.%'
)
)
or
(pn.namespace=':namespace:'
and (instr(pn.namespace, substring_index(pn.project, '.', -1)) != 0)
and (
pn.namespace like 'org.eclipse.%'
)
)
limit 1";
$args = array(':namespace:' => $namespace, ':name:' => $name);
query('dashboard', $sql, $args, function($row) use (&$project) {
$project = $row['id'];
});
return $project;
}
private static function getProjectByMakingAReallyGoodGuess($type, $source, $namespace, $name, $revision) {
global $projectNameSegmentPattern;
// If we don't find an override, try to determine the project ID from
// the namespace and name.
if ("p2" == $type && preg_match('/p2\.eclipse\-(?:plugin|feature)/',$namespace)) {
// When the third segment (namespace) is "p2.eclipse-plugin", we assume that
// the next bit is the shortname for the project and we do a search based on that.
$matches = null;
$pattern = "/^org\\.eclipse\\.({$projectNameSegmentPattern})\\b/";
if (preg_match($pattern, $name, $matches)) {
$projects = Project::getAllWithShortname($matches[1]);
if (count($projects) == 1) {
$project = reset($projects);
return $project->getId();
}
}
} else {
// When the third segment (namespace) starts with "org.eclipse", "org.glassfish",
// "jakarta", or "@eclipse-" we assume/hope that the next bit is the shortname for the project
// and we do a search based on that.
$matches = null;
$pattern = "/^(?:(?:org\\.(?:eclipse|glassfish)|jakarta)\\.|@eclipse-)({$projectNameSegmentPattern})\\b/";
if (preg_match($pattern, $namespace, $matches)) {
$projects = Project::getAllWithShortname($matches[1]);
if (count($projects) == 1) {
$project = reset($projects);
return $project->getId();
}
}
// When the namespaces starts with "org.locationtech.*" or "org.polarsys", look for
// a project with id "locationtech.*" or "polarsys.*".
$matches = null;
$pattern = "/^org\.(locationtech|polarsys)\.({$projectNameSegmentPattern})\\b/";
if (preg_match($pattern, $namespace, $matches)) {
$project = Project::getProject("{$matches[1]}.{$matches[2]}");
if ($project) return $project->getId();
}
}
return null;
}
}