| <?php |
| /******************************************************************************* |
| * Copyright (c) 2009 Eclipse Foundation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Wayne Beaton (Eclipse Foundation)- initial API and implementation |
| *******************************************************************************/ |
| |
| /* |
| * This file assumes that the $App variable has been defined. |
| */ |
| |
| require_once(dirname(__FILE__) . "/common.php"); |
| require_once(dirname(__FILE__) . "/debug.php"); |
| |
| trace_file_info(__FILE__); |
| |
| class CQ { |
| var $id; |
| var $project; |
| var $description; |
| var $name; |
| var $state; |
| var $status; |
| var $license; |
| var $resolution; |
| var $keywords; |
| var $attachments; |
| var $bundles = array(); |
| var $parent; |
| var $piggyback_cqs = array(); |
| |
| function CQ($id, $description, $keywords=array(), $attachments=0) { |
| $this->id = $id; |
| $this->description = $description; |
| $this->keywords = $keywords; |
| $this->attachments = $attachments; |
| } |
| |
| public function is_root() { |
| return !$this->parent; |
| } |
| |
| public function getId() { |
| return $this->id; |
| } |
| |
| /** |
| * Return a reasonable name for the CQ. We attempt to extract this |
| * from the description. In the process, we eliminate as much "chaff" |
| * as possible. The word "Apache" at the front of "Apache Ant" is not |
| * interesting. Nor is anything in brackets or parentheses considered |
| * interesting. You get the idea... |
| * |
| * The computed value is cached the first time this method is called. |
| */ |
| public function getName() { |
| if ($this->name) return $this->name; |
| |
| $name = $this->getDescription(); |
| $name = preg_replace('/Apache/i', '', $name); |
| $name = preg_replace('/Google/i', '', $name); |
| $name = preg_replace('/\[[^\]]*\]/', '', $name); |
| $name = preg_replace('/\([^\)]*\)/', '', $name); |
| $name = preg_replace('/\([^\)]*\)/', '', $name); |
| $name = preg_replace('/Version\:?.*$/i', '', $name); |
| $name = preg_replace('/\-?(\d+(\.\d+)*)/i', '', $name); |
| $name = preg_replace('/\.jar/i', '', $name); |
| $name = preg_replace('/\sjar/i', '', $name); |
| |
| $name = preg_replace('/\sv(\s|$)/i', '', $name); // Remove a solitary 'v' (i.e. "version") |
| |
| $name = preg_replace('/\W+/', ' ', $name); // Replace non-word characters with a single space. |
| |
| $name = ucwords($name); |
| |
| $this->name = trim($name); |
| |
| return $this->name; |
| } |
| |
| /** |
| * Return the version if possible. This value is intended for purposes of sorting |
| * and other more superficial purposes and should generally not be considered |
| * 'official' for purposes of comparing CQ equivalency (this may change). |
| * |
| * We look for the numbers following the word "Version" in the description. If |
| * this fails, we look for anything that looks like a version number. Version numbers |
| * are assumed to be mutli-segment numbers (e.g. '1.32.3'). |
| */ |
| public function getVersion() { |
| if (preg_match('/Version\:?\s+(\d+(\.\d+)*)/i', $this->getDescription(), $matches)) { |
| return $matches[1]; |
| } else if (preg_match('/(\d+(\.\d+)*)/', $this->getDescription(), $matches)) { |
| return $matches[1]; |
| } |
| return null; |
| } |
| |
| public function getDescription() { |
| return $this->description; |
| } |
| |
| public function getLicense() { |
| return $this->license; |
| } |
| |
| public function &get_root($recursion_list = array()) { |
| if (in_array($this, $recursion_list)) throw new RecursiveAncestryException(); |
| if ($this->parent == $this) return $this; |
| if (!$this->parent) return $this; |
| |
| $recursion_list[] = $this; |
| $parent = $this->parent; |
| $root = &$parent->get_root($recursion_list); |
| |
| return $root; |
| } |
| |
| /** |
| * PROVISIONAL |
| * Enter description here ... |
| */ |
| public function get_related() { |
| $related = array(); |
| $this->get_root()->gather_related($related); |
| return $related; |
| } |
| |
| /** |
| * This method finds a related CQ for the project with the provided id. |
| * That is, the receiver and all of it's descendent piggybacks are |
| * searched to find one that is owned by the given project id. |
| * |
| * @param $projectid The id of the project to search for. |
| * @return An instance of CQ or <code>null</code>. |
| */ |
| public function find_cq_for_project($projectid, $check_parents=false, $check_children=false) { |
| if ($this->project == $projectid) return $this; |
| foreach ($this->piggyback_cqs as $piggyback) { |
| $match = $piggyback->find_cq_for_project($projectid); |
| if ($match) return $match; |
| } |
| if ($check_parents) { |
| $parent = get_project_parent_id($projectid); |
| if ($parent) { |
| $match = $this->find_cq_for_project(get_project_parent_id($projectid), true, false); |
| if ($match) return $match; |
| } |
| } |
| if ($check_children) { |
| $match = $this->find_cq_for_subproject($projectid); |
| if ($match) return $match; |
| } |
| |
| return null; |
| } |
| |
| /* private */ function find_cq_for_subproject($projectid) { |
| if (is_valid_subproject_id($projectid, $this->project)) { |
| return $this; |
| } |
| foreach ($this->piggyback_cqs as $piggyback) { |
| $match = $piggyback->find_cq_for_subproject($projectid); |
| if ($match) return $match; |
| } |
| return null; |
| } |
| |
| /* private */ function gather_related(&$related) { |
| $related[$this->id] = $this; |
| foreach ($this->piggyback_cqs as $piggyback) { |
| $piggyback->gather_related($related); |
| } |
| } |
| |
| public function as_html() { |
| return "<a href=\"https://dev.eclipse.org/ipzilla/show_bug.cgi?id=$this->id\">CQ $this->id</a>"; |
| } |
| |
| /** |
| * @deprecated |
| * @see #isContribution() |
| * return bool |
| */ |
| public function is_contribution() { |
| return $this->isContribution(); |
| } |
| |
| /** |
| * Answers true if the receiver represents a contribution. That is, does it |
| * represent something that has found (or will find) its way into an |
| * eclipse.org VCS repository? |
| * |
| * @return bool |
| */ |
| public function isContribution() { |
| if ($this->hasKeyword('epl')) return true; |
| if ($this->hasKeyword('projectcode')) return true; |
| return false; |
| } |
| |
| /** |
| * @deprecated |
| * @see #isThirdParty() |
| * @return bool |
| */ |
| public function is_third_party() { |
| return $this->isThirdParty(); |
| } |
| |
| /** |
| * Return true if the receiver represents a third-party library, false |
| * otherwise. |
| * |
| * @return bool |
| */ |
| public function isThirdParty() { |
| if ($this->hasKeyword("nonepl")) return true; |
| if ($this->hasKeyword("thirdparty")) return true; |
| return false; |
| } |
| |
| public function isActive() { |
| switch ($this->state) { |
| case 'approved_all_projects': |
| case 'approved_one_project': |
| case 'approved': |
| case 'reuse': |
| return true; |
| |
| case 'awaiting_analysis': |
| case 'awaiting_committer': |
| case 'awaiting_emo': |
| case 'awaiting_pmc': |
| case 'awaiting_project': |
| case 'awaiting_triage': |
| case 'new': |
| case 'under_review': |
| if ($this->isFixed()) return true; |
| } |
| |
| return false; |
| } |
| |
| public function isPrereq() { |
| switch ($this->state) { |
| case 'exempt_prereq': |
| case 'workswith': |
| case 'prereq': |
| return true; |
| } |
| return false; |
| } |
| |
| public function isExemptPrereq() { |
| return $this->state == 'exempt_prereq'; |
| } |
| |
| public function isWorksWith() { |
| return $this->state == 'workswith'; |
| } |
| |
| public function isPending() { |
| switch ($this->state) { |
| case 'awaiting_analysis': |
| case 'awaiting_committer': |
| case 'awaiting_emo': |
| case 'awaiting_pmc': |
| case 'awaiting_project': |
| case 'awaiting_triage': |
| case 'new': |
| case 'under_review': |
| if (!$this->isFixed()) return true; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * A CQ is considered unused if it has any of the following keywords: |
| * 'unused', 'obsolete', or 'withdrawn'. |
| * |
| * @return bool |
| */ |
| public function isUnused() { |
| if ($this->hasKeyword('unused')) return true; |
| if ($this->hasKeyword('obsolete')) return true; |
| if ($this->hasKeyword('withdrawn')) return true; |
| return false; |
| } |
| |
| public function isModified() { |
| return $this->hasKeyword('modified'); |
| } |
| |
| public function isUnmodified() { |
| return $this->hasKeyword('modified'); |
| } |
| |
| public function isSource() { |
| return $this->hasKeyword('source'); |
| } |
| |
| public function isBinary() { |
| return $this->hasKeyword('binary'); |
| } |
| |
| public function isSourceAndBinary() { |
| return $this->hasKeyword('sourceandbinary'); |
| } |
| |
| /** |
| * @deprecated |
| * @see #hasKeyword() |
| * @param string $needle |
| */ |
| public function has_keyword($needle) { |
| return $this->hasKeyword($needle); |
| } |
| |
| /** |
| * Answers true if the receiver has the provided keyword applied to it, or |
| * false otherwise. |
| * |
| * @param string $needle |
| */ |
| public function hasKeyword($needle) { |
| foreach ($this->keywords as $keyword) { |
| if ($keyword == $needle) return true; |
| } |
| return false; |
| } |
| |
| public function is_resolved() { |
| if (!$this->status) return false; |
| return in_array($this->status, array('RESOLVED', 'VERIFIED', 'CLOSED')); |
| } |
| |
| public function isFixed() { |
| if (!$this->is_resolved()) return false; |
| return $this->resolution == 'FIXED'; |
| } |
| |
| public function is_approved() { |
| if (!$this->is_resolved()) return false; |
| return $this->state == 'approved'; |
| } |
| |
| public function is_invalid() { |
| if (!$this->is_resolved()) return false; |
| if (!$this->resolution) return false; |
| return in_array($this->resolution, array('INVALID', 'WONTFIX', 'DUPLICATE', 'WORKSFORME', 'MOVED')); |
| } |
| |
| public function add_bundle($bundle, $regex) { |
| $this->bundles[$bundle] = $regex; |
| } |
| |
| public function get_parent_id() { |
| if (preg_match('/ATO\s+Orbit\s+(\d+)/', $this->description, $matches)) |
| return $matches[1]; |
| |
| if (preg_match('/ATO\s*CQ\s*([0-9]+)/', $this->description, $matches)) |
| return $matches[1]; |
| |
| if (preg_match('/PB\s*CQ\s*([0-9]+)/', $this->description, $matches)) |
| return $matches[1]; |
| |
| if (preg_match('/PB\s*([0-9]+)/', $this->description, $matches)) |
| return $matches[1]; |
| |
| if (preg_match('/Orbit\s*(?:CQ)?\s*([0-9]+)/', $this->description, $matches)) |
| return $matches[1]; |
| |
| // $matches = null; |
| // preg_match('/CQ[ ]*#?([0-9]*)/', $this->description, $matches); |
| // if (count($matches) > 1) return $matches[1]; |
| |
| return null; |
| } |
| } |
| |
| define("CQ_SHOW_NONE", 0); |
| define("CQ_SHOW_ANCESTORS", 1); |
| define("CQ_SHOW_PIGGYBACKS", 2); |
| define("CQ_SHOW_BUNDLES", 4); |
| define("CQ_SHOW_KEYWORDS", 8); |
| define("CQ_SHOW_ALL", CQ_SHOW_ANCESTORS + CQ_SHOW_PIGGYBACKS + CQ_SHOW_BUNDLES + CQ_SHOW_KEYWORDS); |
| |
| function to_node($doc, $root, $cq, $options = CQ_SHOW_ALL, $tag = 'cq', $recursion_list=array()) { |
| $cq_element = $doc->createElement($tag); |
| $cq_element->setAttribute('id', $cq->id); |
| $cq_element->setAttribute('name', $cq->getName()); |
| $cq_element->setAttribute('version', $cq->getVersion()); |
| $cq_element->setAttribute('project', $cq->project); |
| $cq_element->setAttribute('status', $cq->status); |
| $cq_element->setAttribute('state', $cq->state); |
| $cq_element->setAttribute('resolution', $cq->resolution); |
| $cq_element->setAttribute('description', xmlentities($cq->description)); |
| $cq_element->setAttribute('attachments', $cq->attachments); |
| $cq_element->setAttribute('third-party', $cq->is_third_party() ? "true" : "false"); |
| $cq_node = $root->appendChild($cq_element); |
| |
| if ($options & CQ_SHOW_KEYWORDS) { |
| foreach ($cq->keywords as $keyword) { |
| $keyword_element = $doc->createElement('keyword'); |
| $keyword_element->setAttribute('name', $keyword); |
| $cq_node->appendChild($keyword_element); |
| } |
| } |
| |
| if ($options & CQ_SHOW_BUNDLES) { |
| foreach ($cq->bundles as $bundle => $regex) { |
| $bundle_element = $doc->createElement('bundle'); |
| $bundle_element->setAttribute('id', $bundle); |
| $bundle_element->setAttribute('regex', $regex); |
| $cq_node->appendChild($bundle_element); |
| } |
| } |
| |
| if ($options & CQ_SHOW_PIGGYBACKS) { |
| foreach($cq->piggyback_cqs as $piggyback) { |
| to_node($doc, $cq_node, $piggyback, CQ_SHOW_NONE, 'piggyback'); |
| } |
| } |
| |
| if ($options & CQ_SHOW_ANCESTORS) { |
| // If this is the second time that we've encountered the receiver |
| // in the recursion, bail out to avoid an infinite loop. |
| if (in_array($cq, $recursion_list)) { |
| $parent_element = $doc->createElement('recursive-parent'); |
| $cq_node->appendChild($parent_element); |
| return; |
| } |
| |
| // If there's a parent, then we're going to recurse. If not, just bail. |
| if (!$cq->parent) return; |
| |
| $recursion_list[] = $cq; |
| to_node($doc, $cq_node, $cq->parent, CQ_SHOW_ANCESTORS, 'parent-cq', $recursion_list); |
| } |
| } |
| |
| class RecursiveAncestryException extends Exception {} |
| |
| function find_root_cqs() { |
| $cqs = array(); |
| foreach(find_cqs() as $cq) { |
| if ($cq->is_root()) $cqs[] = $cq; |
| } |
| return $cqs; |
| } |
| |
| /** |
| * This function finds a single CQ. Note that multiple calls to this |
| * function will return the same object. |
| * |
| * @param $id int id of the the CQ to find. |
| */ |
| function find_cq($id) { |
| $cqs = find_cqs(); |
| return $cqs[$id]; |
| } |
| |
| /** |
| * This function, curiously enough, finds the known CQs. The computation |
| * is done exactly once. Multiple calls to this function will return the |
| * same array. |
| * |
| * If this function is called on the server, it finds the CQs from the database. |
| * If it is called in "Development Mode", it makes a RESTful webservice call |
| * to the server to get the CQ data. |
| * |
| */ |
| function find_cqs() { |
| // TODO Consider implementing a completely offline mode. |
| global $App; |
| global $_cqs; |
| |
| if (!isset($_cqs)) $_cqs = $App->devmode ? load_cqs_from_server() : primitive_find_cqs(); |
| |
| return $_cqs; |
| } |
| |
| /** |
| * This function returns the CQ assigned to the project with the provided id |
| * that contributes the bundle with the provided name. |
| */ |
| function &find_cq_for_bundle($bundle, $projectid, $check_parents=false, $check_children=false) { |
| foreach (find_root_cqs() as $cq) { |
| foreach($cq->bundles as $pattern => $regex) { |
| if (@preg_match($regex, $bundle)) { |
| $match = $cq->find_cq_for_project($projectid, $check_parents, $check_children); |
| if ($match) return $match; |
| } |
| } |
| } |
| $match = null; |
| return $match; |
| } |
| |
| function find_cqs_for_bundle($bundle) { |
| $cqs = array(); |
| foreach (find_root_cqs() as $cq) { |
| foreach($cq->bundles as $pattern => $regex) { |
| if (@preg_match($regex, $bundle)) { |
| $cq->gather_related($cqs); |
| } |
| } |
| } |
| return $cqs; |
| } |
| |
| /** |
| * This function does the dirty work of actually finding the CQs from the IPZilla Database. |
| * |
| * THIS IS NOT PUBLIC API. |
| */ |
| function primitive_find_cqs() { |
| global $App; |
| |
| $sql = " |
| SELECT |
| bugs.bug_id as id, |
| components.name as project, |
| bugs.short_desc as description, |
| bugs.bug_status as status, |
| bugs.resolution as resolution, |
| bugs.keywords as keywords, |
| bugs.bug_severity as state, |
| bugs.cf_license as license, |
| count(attachments.attach_id) as attachments |
| FROM |
| bugs |
| join components on (bugs.component_id = components.id) |
| left join attachments on (bugs.bug_id = attachments.bug_id and attachments.isobsolete = 0) |
| group by bugs.bug_id"; |
| |
| $result = $App->ipzilla_sql( $sql ); |
| |
| $cqs = array(); |
| |
| while( $row = mysql_fetch_assoc($result) ) { |
| $id = $row['id']; |
| $keywords = preg_split('/, */', $row['keywords']); |
| $cq = new CQ($id,$row['description'],$keywords,$row['attachments']); |
| $cq->project = $row['project']; |
| $cq->status = $row['status']; |
| $cq->resolution = $row['resolution']; |
| $cq->state = $row['state']; |
| $cq->license = $row['license']; |
| $cqs[$id] = $cq; |
| } |
| |
| matchParentCQs($cqs); |
| |
| foreach ($cqs as $cq) { |
| try { |
| $root = $cq->get_root(); |
| } catch (RecursiveAncestryException $e) { |
| continue; |
| } |
| if (!$root) continue; |
| if ($root == $cq) continue; |
| $root->piggyback_cqs[] = $cq; |
| } |
| |
| read_bundle_mappings_file($cqs); |
| read_orbit_bundle_mappings_file($cqs); |
| |
| return $cqs; |
| } |
| |
| function matchParentCQs(&$cqs) { |
| $trace = trace("Matching CQs with parents"); |
| $previous = array(); |
| foreach($cqs as $cq) { |
| $nested = $trace->trace("Finding parent for CQ" . $cq->id . ", " . $cq->description); |
| $parent_id = $cq->get_parent_id(); |
| if ($parent_id == $cq->id) continue; |
| if ($parent_id) { |
| $nested->trace("Parent is indicated: " . $parent_id); |
| $cq->parent = $cqs[$parent_id]; |
| continue; |
| } |
| $description = $cq->getDescription(); |
| foreach($previous as $candidate) { |
| if ($candidate->getDescription() == $description) { |
| $nested->trace("Found similar: " . $candidate->id); |
| $cq->parent = $candidate; |
| } |
| } |
| $previous[] = $cq; |
| } |
| } |
| |
| /** |
| * This function reads the bundle mappings from the cq-map.txt file |
| * and--when possible--connects the bundle name with a cq. Each line in the |
| * text that matches the pattern '[cq#], [bundle name]' is assumed to |
| * be a valid mapping. |
| * |
| * This function returns nothing; it has the side effect of (potentially) |
| * modifying the CQ instances in the first parameter. |
| * |
| * THIS IS NOT PUBLIC API. |
| * |
| * @param unknown_type $cqs |
| */ |
| function read_bundle_mappings_file(&$cqs) { |
| $top = trace("Reading bundle mappings."); |
| |
| $filepath = $_SERVER['DOCUMENT_ROOT'] . '/projects/ip-check/cq-map.txt'; |
| $mappings = file($filepath, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); |
| |
| foreach($mappings as $mapping) { |
| $tracer = $top->trace("Processing bundle mapping '$mapping'."); |
| |
| $matches = null; |
| preg_match('/\s*([0-9]+),\s*(.+\.jar)/', $mapping, $matches); |
| if (count($matches) > 2) { |
| $id = $matches[1]; |
| $bundle = $matches[2]; |
| $tracer->trace("Found CQ/bundle mapping: CQ $id maps to $bundle"); |
| |
| if (isset($cqs[$id])) { |
| // First, we find the CQ that corresponds to the mapping |
| $cq = $cqs[$id]; |
| try { |
| // Then, we find the root CQ (sometimes the mapping is |
| // between a piggyback CQ and bundle). |
| $cq = $cq->get_root(); |
| if ($cq->id != $id) { |
| $tracer->trace("CQ $id has root $cq->id"); |
| } |
| } catch (RecursiveAncestryException $e) { |
| $tracer->trace("Cannot find root for CQ $cq."); |
| continue; |
| } |
| |
| |
| $root = $cq->get_root(); |
| /* |
| * The regex patterns formed below have a start delimiter (^) |
| * but no end delimiter ($) to allow for matches of files with |
| * extra junk (e.g. .jar.pack.gz) at the end. This may end up |
| * being more flexible than we need. |
| */ |
| $matches = null; |
| preg_match('/^([a-zA-Z0-9\.\-\_]+)[_-]((?:\d+\.){3})(.*\.)?jar/', $bundle, $matches); |
| if (count($matches) > 0) { |
| $pattern = $matches[1] . '_' . $matches[2]; |
| $regex = $matches[1] . '[_-]' . $matches[2]; |
| $regex = str_replace('.', '\.', $regex); |
| $regex = '/^' . $regex . '(.*\.)?jar/'; |
| $pattern = "$pattern*.jar"; |
| |
| $root->add_bundle($pattern, $regex); |
| $tracer->trace("Bundle '$bundle' becomes pattern '$pattern' ($regex)."); |
| |
| // Only generate a source bundle mapping if this isn't a source bundle. |
| if (!preg_match('/\.source$/', $matches[1])) { |
| $sourcePattern = $matches[1] . '.source_' . $matches[2]; |
| $sourceRegex = $matches[1] . '.source[_-]' . $matches[2]; |
| $sourceRegex = str_replace('.', '\.', $sourceRegex); |
| $sourceRegex = '/^' . $sourceRegex . '(.*\.)?jar/'; |
| $sourcePattern = "$sourcePattern*.jar"; |
| |
| $root->add_bundle($sourcePattern, $sourceRegex); |
| $tracer->trace("Source for bundle '$bundle' becomes pattern '$sourcePattern' ($sourceRegex)."); |
| } |
| } else { |
| $pattern = $bundle; |
| $regex = str_replace('.', '\.', $pattern); |
| $regex = "/^$regex/"; |
| |
| $tracer->trace("Bundle '$bundle' becomes pattern '$pattern' ($regex)."); |
| $root->add_bundle($pattern, $regex); |
| } |
| } else { |
| $tracer->trace("CQ $id not found!"); |
| } |
| } else { |
| $tracer->trace("Skipping CQ-bundle-mapping: $mapping (incomplete information)"); |
| } |
| } |
| } |
| |
| /** |
| * This function reads the bundle mappings from Orbit. Note that this |
| * does not read directly from Orbit, but rather from a dump of information |
| * extracted from Orbit in JSON format. |
| * |
| * This function returns nothing; it has the side effect of (potentially) |
| * modifying the CQ instances in the first parameter. |
| * |
| * THIS IS NOT PUBLIC API. |
| * |
| * @param unknown_type $cqs |
| */ |
| function read_orbit_bundle_mappings_file(&$cqs) { |
| // TODO Refactoring opportunities abound. |
| $top = trace("Reading Orbit bundle mappings."); |
| |
| $filepath = dirname(__FILE__) . '/../ip-check/orbit-cq-map.json'; |
| $orbit = json_decode(file_get_contents($filepath), TRUE); |
| |
| foreach($orbit as $bundle => $data) { |
| $tracer = $top->trace("Processing bundle mapping '$bundle'."); |
| |
| if ($id = $data['cq']) { |
| $tracer->trace("Found CQ/bundle mapping: CQ $id maps to $bundle"); |
| |
| if (isset($cqs[$id])) { |
| // First, we find the CQ that corresponds to the mapping |
| $cq = $cqs[$id]; |
| try { |
| // Then, we find the root CQ (sometimes the mapping is |
| // between a piggyback CQ and bundle). |
| $cq = $cq->get_root(); |
| if ($cq->id != $id) { |
| $tracer->trace("CQ $id has root $cq->id"); |
| } |
| } catch (RecursiveAncestryException $e) { |
| $tracer->trace("Cannot find root for CQ $cq."); |
| continue; |
| } |
| |
| $root = $cq->get_root(); |
| /* |
| * The regex patterns formed below have a start delimiter (^) |
| * but no end delimiter ($) to allow for matches of files with |
| * extra junk (e.g. .jar.pack.gz) at the end. This may end up |
| * being more flexible than we need. |
| */ |
| $matches = null; |
| preg_match('/^([a-zA-Z0-9\.\-\_]+)[_-]((?:\d+\.){3})(.*\.)?jar/', $bundle, $matches); |
| if (count($matches) > 0) { |
| $pattern = $matches[1] . '_' . $matches[2]; |
| $regex = $matches[1] . '[_-]' . $matches[2]; |
| $regex = str_replace('.', '\.', $regex); |
| $regex = '/^' . $regex . '(.*\.)?jar/'; |
| $pattern = "$pattern*.jar"; |
| |
| $root->add_bundle($pattern, $regex); |
| $tracer->trace("Bundle '$bundle' becomes pattern '$pattern' ($regex)."); |
| |
| // Only generate a source bundle mapping if this isn't a source bundle. |
| if (!preg_match('/\.source$/', $matches[1])) { |
| $sourcePattern = $matches[1] . '.source_' . $matches[2]; |
| $sourceRegex = $matches[1] . '.source[_-]' . $matches[2]; |
| $sourceRegex = str_replace('.', '\.', $sourceRegex); |
| $sourceRegex = '/^' . $sourceRegex . '(.*\.)?jar/'; |
| $sourcePattern = "$sourcePattern*.jar"; |
| |
| $root->add_bundle($sourcePattern, $sourceRegex); |
| $tracer->trace("Source for bundle '$bundle' becomes pattern '$sourcePattern' ($sourceRegex)."); |
| } |
| } else { |
| $pattern = $bundle; |
| $regex = str_replace('.', '\.', $pattern); |
| $regex = "/^$regex/"; |
| |
| $tracer->trace("Bundle '$bundle' becomes pattern '$pattern' ($regex)."); |
| $root->add_bundle($pattern, $regex); |
| } |
| } else { |
| $tracer->trace("CQ $id not found!"); |
| } |
| } else { |
| $tracer->trace("Skipping CQ-bundle-mapping: $bundle (incomplete information)"); |
| } |
| } |
| } |
| |
| /** |
| * PROVISIONAL |
| * @param $id |
| */ |
| function find_root_cq($id) { |
| $cqs = find_cqs(); |
| if (!isset($cqs[$id])) return null; |
| $cq = $cqs[$id]; |
| if (!$cq) return null; |
| return $cq->get_root(); |
| } |
| |
| /** |
| * PROVISIONAL |
| * @param $ids |
| * @param $projectid |
| */ |
| function find_related_cq($ids, $projectid) { |
| foreach ($ids as $id) { |
| $cq = find_root_cq($id); |
| if (!$cq) continue; |
| $related = $cq->find_cq_for_project($projectid); |
| if ($related) return $related; |
| } |
| return null; |
| } |
| |
| /** |
| * Force the CQs to be loaded from the server via HTTP. Otherwise, CQs will be lazily |
| * loaded from the database when they are required. |
| */ |
| function load_cqs_from_server($file = "http://www.eclipse.org/projects/xml/cqs.php") { |
| global $_cqs; |
| $_cqs = array(); |
| $raw = simplexml_load_file($file); |
| |
| foreach($raw->cq as $item) { |
| $id = (int)$item['id']; |
| $description = $item['description']; |
| $description = "$description"; |
| |
| $cq = new CQ($id, $description); |
| $cq->project = $item['project']; |
| $cq->status = $item['status']; |
| $cq->state = $item['state']; |
| |
| foreach ($item->keyword as $keyword) { |
| $cq->keywords[] = $keyword['name']; |
| } |
| |
| foreach ($item->bundle as $bundle) { |
| $cq->add_bundle((String)$bundle['id'], (String)$bundle['regex']); |
| } |
| |
| $_cqs[$id] = $cq; |
| } |
| |
| foreach($raw->cq as $item) { |
| $id = (int)$item['id']; |
| $cq = $_cqs[$id]; |
| |
| foreach ($item->piggyback as $piggyback) { |
| $cq->piggyback_cqs[] = $_cqs[(int)$piggyback['id']]; |
| } |
| } |
| |
| //read_bundle_mappings_file($cqs); |
| |
| return $_cqs; |
| } |
| |
| ?> |