Tree::findNextTerm()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
cc 5
eloc 11
c 0
b 0
f 0
nc 6
nop 3
dl 0
loc 22
rs 9.6111
ccs 0
cts 11
cp 0
crap 30
1
<?php
2
3
/**
4
 * File holding the Lingo\Tree class
5
 *
6
 * This file is part of the MediaWiki extension Lingo.
7
 *
8
 * @copyright 2011 - 2018, Stephan Gambke
9
 * @license GPL-2.0-or-later
10
 *
11
 * The Lingo extension is free software: you can redistribute it and/or modify
12
 * it under the terms of the GNU General Public License as published by the Free
13
 * Software Foundation; either version 2 of the License, or (at your option) any
14
 * later version.
15
 *
16
 * The Lingo extension is distributed in the hope that it will be useful, but
17
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
19
 * details.
20
 *
21
 * You should have received a copy of the GNU General Public License along
22
 * with this program. If not, see <http://www.gnu.org/licenses/>.
23
 *
24
 * @author Stephan Gambke
25
 *
26
 * @file
27
 * @ingroup Lingo
28
 */
29
namespace Lingo;
30
31
/**
32
 * The Lingo\Tree class.
33
 *
34
 * Vocabulary:
35
 * Term - The term as a normal string
36
 * Definition - Its definition (any object)
37
 * Element - An element (leaf) in the glossary tree
38
 * Path - The path in the tree to the leaf representing a term
39
 *
40
 * The glossary is organized as a tree (nested arrays) where the path to the
41
 * definition of a term is the lexemes of the term followed by -1 as the end
42
 * marker.
43
 *
44
 * Example:
45
 * The path to the definition of the term "foo bar baz" would be
46
 * 'foo'.' '.'bar'.' '.'baz'.'-1'. It could thus be accessed as
47
 * $mTree['foo'][' ']['bar'][' ']['baz'][-1]
48
 *
49
 * @ingroup Lingo
50
 */
51
class Tree {
52
53
	const TREE_VERSION = 2;
54
55
	private $mTree = [];
56
	private $mList = [];
57
	private $mMinLength = 1000;
58
59
	/**
60
	 * Adds a string to the Lingo Tree
61
	 *
62
	 * @param string &$term
63
	 * @param array $definition
64
	 */
65
	public function addTerm( &$term, $definition ) {
66
		if ( !$term ) {
67
			return;
68
		}
69
70
		if ( isset( $this->mList[ $term ] ) ) { // term exists, store 2nd definition
71
			$this->mList[ $term ]->addDefinition( $definition );
72
		} else {
73
74
			$matches = [];
75
			preg_match_all( LingoParser::getInstance()->regex, $term, $matches );
76
77
			$element = $this->addElement( $matches[ 0 ], $term, $definition );
78
			$this->mList[ $term ] = &$element[ -1 ];
79
80
			$this->mMinLength = min( [ $this->mMinLength, strlen( $term ) ] );
81
		}
82
	}
83
84
	/**
85
	 * Adds an element to the Lingo Tree
86
	 *
87
	 * @param array &$path An array containing the constituing lexemes of the term
88
	 * @param String &$term
89
	 * @param array &$definition
90
	 * @return array the tree node the element was stored in
91
	 */
92
	protected function &addElement( array &$path, &$term, &$definition ) {
93
		$tree = &$this->mTree;
94
95
		// end of path, store description; end of recursion
96
		while ( ( $step = array_shift( $path ) ) !== null ) {
97
98
			if ( !isset( $tree[ $step ] ) ) {
99
				$tree[ $step ] = [];
100
			}
101
102
			$tree = &$tree[ $step ];
103
		}
104
105
		if ( isset( $tree[ -1 ] ) ) {
106
			$tree[ -1 ]->addDefinition( $definition );
107
		} else {
108
			$tree[ -1 ] = new Element( $term, $definition );
109
		}
110
111
		return $tree;
112
	}
113
114
	/**
115
	 * @return int
116
	 */
117
	public function getMinTermLength() {
118
		return $this->mMinLength;
119
	}
120
121
	/**
122
	 * @return array
123
	 */
124
	public function getTermList() {
125
		return $this->mList;
126
	}
127
128
	/**
129
	 * @param array &$lexemes
130
	 * @param int $index
131
	 * @param int $countLexemes
132
	 *
133
	 * @return array
134
	 */
135
	public function findNextTerm( &$lexemes, $index, $countLexemes ) {
136
		$start = $lastindex = $index;
137
		$definition = null;
138
139
		// skip until the start of a term is found
140
		while ( $index < $countLexemes && !$definition ) {
141
			$currLex = &$lexemes[ $index ][ 0 ];
142
143
			// Did we find the start of a term?
144
			if ( array_key_exists( $currLex, $this->mTree ) ) {
145
				list( $lastindex, $definition ) = $this->findNextTermNoSkip( $this->mTree[ $currLex ], $lexemes, $index, $countLexemes );
146
			}
147
148
			// this will increase the index even if we found something;
149
			// will be corrected after the loop
150
			$index++;
151
		}
152
153
		if ( $definition ) {
154
			return [ $index - $start - 1, $lastindex - $index + 2, $definition ];
155
		} else {
156
			return [ $index - $start, 0, null ];
157
		}
158
	}
159
160
	/**
161
	 * @param array &$tree
162
	 * @param array &$lexemes
163
	 * @param int $index
164
	 * @param int $countLexemes
165
	 *
166
	 * @return array
167
	 */
168
	public function findNextTermNoSkip( array &$tree, &$lexemes, $index, $countLexemes ) {
169
		if ( $index + 1 < $countLexemes && array_key_exists( $currLex = $lexemes[ $index + 1 ][ 0 ], $tree ) ) {
170
			$ret = $this->findNextTermNoSkip( $tree[ $currLex ], $lexemes, $index + 1, $countLexemes );
171
		} else {
172
			$ret = [ $index, &$tree[ -1 ] ];
173
		}
174
175
		return $ret;
176
	}
177
178
}
179