Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/src/applications/fact/chart/PhabricatorChartFunction.php
12256 views
1
<?php
2
3
abstract class PhabricatorChartFunction
4
extends Phobject {
5
6
private $argumentParser;
7
private $functionLabel;
8
9
final public function getFunctionKey() {
10
return $this->getPhobjectClassConstant('FUNCTIONKEY', 32);
11
}
12
13
final public static function getAllFunctions() {
14
return id(new PhutilClassMapQuery())
15
->setAncestorClass(__CLASS__)
16
->setUniqueMethod('getFunctionKey')
17
->execute();
18
}
19
20
final public function setArguments(array $arguments) {
21
$parser = $this->getArgumentParser();
22
$parser->setRawArguments($arguments);
23
24
$specs = $this->newArguments();
25
26
if (!is_array($specs)) {
27
throw new Exception(
28
pht(
29
'Expected "newArguments()" in class "%s" to return a list of '.
30
'argument specifications, got %s.',
31
get_class($this),
32
phutil_describe_type($specs)));
33
}
34
35
assert_instances_of($specs, 'PhabricatorChartFunctionArgument');
36
37
foreach ($specs as $spec) {
38
$parser->addArgument($spec);
39
}
40
41
$parser->setHaveAllArguments(true);
42
$parser->parseArguments();
43
44
return $this;
45
}
46
47
public function setFunctionLabel(PhabricatorChartFunctionLabel $label) {
48
$this->functionLabel = $label;
49
return $this;
50
}
51
52
public function getFunctionLabel() {
53
if (!$this->functionLabel) {
54
$this->functionLabel = id(new PhabricatorChartFunctionLabel())
55
->setName(pht('Unlabeled Function'))
56
->setColor('rgba(255, 0, 0, 1)')
57
->setFillColor('rgba(255, 0, 0, 0.15)');
58
}
59
60
return $this->functionLabel;
61
}
62
63
final public function getKey() {
64
return $this->getFunctionLabel()->getKey();
65
}
66
67
final public static function newFromDictionary(array $map) {
68
PhutilTypeSpec::checkMap(
69
$map,
70
array(
71
'function' => 'string',
72
'arguments' => 'list<wild>',
73
));
74
75
$functions = self::getAllFunctions();
76
77
$function_name = $map['function'];
78
if (!isset($functions[$function_name])) {
79
throw new Exception(
80
pht(
81
'Attempting to build function "%s" from dictionary, but that '.
82
'function is unknown. Known functions are: %s.',
83
$function_name,
84
implode(', ', array_keys($functions))));
85
}
86
87
$function = id(clone $functions[$function_name])
88
->setArguments($map['arguments']);
89
90
return $function;
91
}
92
93
public function getSubfunctions() {
94
$result = array();
95
$result[] = $this;
96
97
foreach ($this->getFunctionArguments() as $argument) {
98
foreach ($argument->getSubfunctions() as $subfunction) {
99
$result[] = $subfunction;
100
}
101
}
102
103
return $result;
104
}
105
106
public function getFunctionArguments() {
107
$results = array();
108
109
$parser = $this->getArgumentParser();
110
foreach ($parser->getAllArguments() as $argument) {
111
if ($argument->getType() !== 'function') {
112
continue;
113
}
114
115
$name = $argument->getName();
116
$value = $this->getArgument($name);
117
118
if (!is_array($value)) {
119
$results[] = $value;
120
} else {
121
foreach ($value as $arg_value) {
122
$results[] = $arg_value;
123
}
124
}
125
}
126
127
return $results;
128
}
129
130
public function newDatapoints(PhabricatorChartDataQuery $query) {
131
$xv = $this->newInputValues($query);
132
133
if ($xv === null) {
134
$xv = $this->newDefaultInputValues($query);
135
}
136
137
$xv = $query->selectInputValues($xv);
138
139
$n = count($xv);
140
$yv = $this->evaluateFunction($xv);
141
142
$points = array();
143
for ($ii = 0; $ii < $n; $ii++) {
144
$y = $yv[$ii];
145
146
if ($y === null) {
147
continue;
148
}
149
150
$points[] = array(
151
'x' => $xv[$ii],
152
'y' => $y,
153
);
154
}
155
156
return $points;
157
}
158
159
abstract protected function newArguments();
160
161
final protected function newArgument() {
162
return new PhabricatorChartFunctionArgument();
163
}
164
165
final protected function getArgument($key) {
166
return $this->getArgumentParser()->getArgumentValue($key);
167
}
168
169
final protected function getArgumentParser() {
170
if (!$this->argumentParser) {
171
$parser = id(new PhabricatorChartFunctionArgumentParser())
172
->setFunction($this);
173
174
$this->argumentParser = $parser;
175
}
176
return $this->argumentParser;
177
}
178
179
abstract public function evaluateFunction(array $xv);
180
abstract public function getDataRefs(array $xv);
181
abstract public function loadRefs(array $refs);
182
183
public function getDomain() {
184
return null;
185
}
186
187
public function newInputValues(PhabricatorChartDataQuery $query) {
188
return null;
189
}
190
191
public function loadData() {
192
return;
193
}
194
195
protected function newDefaultInputValues(PhabricatorChartDataQuery $query) {
196
$x_min = $query->getMinimumValue();
197
$x_max = $query->getMaximumValue();
198
$limit = $query->getLimit();
199
200
return $this->newLinearSteps($x_min, $x_max, $limit);
201
}
202
203
protected function newLinearSteps($src, $dst, $count) {
204
$count = (int)$count;
205
$src = (int)$src;
206
$dst = (int)$dst;
207
208
if ($count === 0) {
209
throw new Exception(
210
pht('Can not generate zero linear steps between two values!'));
211
}
212
213
if ($src === $dst) {
214
return array($src);
215
}
216
217
if ($count === 1) {
218
return array($src);
219
}
220
221
$is_reversed = ($src > $dst);
222
if ($is_reversed) {
223
$min = (double)$dst;
224
$max = (double)$src;
225
} else {
226
$min = (double)$src;
227
$max = (double)$dst;
228
}
229
230
$step = (double)($max - $min) / (double)($count - 1);
231
232
$steps = array();
233
for ($cursor = $min; $cursor <= $max; $cursor += $step) {
234
$x = (int)round($cursor);
235
236
if (isset($steps[$x])) {
237
continue;
238
}
239
240
$steps[$x] = $x;
241
}
242
243
$steps = array_values($steps);
244
245
if ($is_reversed) {
246
$steps = array_reverse($steps);
247
}
248
249
return $steps;
250
}
251
}
252
253