| <?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; |
| } |
| } |