1 /*
2  * Copyright (c) 2020-2021 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "components/chart_component.h"
17 #include "ace_log.h"
18 #include "ace_mem_base.h"
19 #include "flex_layout.h"
20 #include "font/ui_font_header.h"
21 #include "keys.h"
22 #include "ui_chart.h"
23 #include "ui_circle_progress.h"
24 
25 namespace OHOS {
26 namespace ACELite {
27 const char * const ChartComponent::DATA = "data";
28 const char * const ChartComponent::APPEND_SERIAL_INDEX = "serial";
29 const char * const ChartComponent::LINE = "line";
30 const char * const ChartComponent::BAR = "bar";
31 const char * const ChartComponent::FUNC_JS_API = "append";
32 const char * const ChartComponent::STROKE_COLOR = "strokeColor";
33 const char * const ChartComponent::STROKE_WIDTH = "strokeWidth";
34 const char * const ChartComponent::FILL_COLOR = "fillColor";
35 const char * const ChartComponent::GRADIENT = "gradient";
36 const char * const ChartComponent::LINE_STYLE = "lineStyle";
37 const char * const ChartComponent::SMOOTH = "smooth";
38 const char * const ChartComponent::HEAD_POINT = "headPoint";
39 const char * const ChartComponent::SHAPE = "shape";
40 const char * const ChartComponent::SIZE = "size";
41 const char * const ChartComponent::TOP_POINT = "topPoint";
42 const char * const ChartComponent::BOTTOM_POINT = "bottomPoint";
43 const char * const ChartComponent::X_AXIS = "xAxis";
44 const char * const ChartComponent::Y_AXIS = "yAxis";
45 const char * const ChartComponent::MIN = "min";
46 const char * const ChartComponent::MAX = "max";
47 const char * const ChartComponent::TYPE = "type";
48 const char * const ChartComponent::SERIES = "series";
49 const char * const ChartComponent::COLOR = "color";
50 const char * const ChartComponent::WIDTH = "width";
51 const char * const ChartComponent::LOOP = "loop";
52 const char * const ChartComponent::MARGIN = "margin";
53 const char * const ChartComponent::DISPLAY = "display";
54 
ChartComponent(jerry_value_t options,jerry_value_t children,AppStyleManager * styleManager)55 ChartComponent::ChartComponent(jerry_value_t options, jerry_value_t children, AppStyleManager* styleManager)
56     : Component(options, children, styleManager),
57       xAxis_(nullptr),
58       yAxis_(nullptr),
59       yMinValue_(0),
60       yMaxValue_(0),
61       xMinValue_(0),
62       xMaxValue_(0),
63       options_(options),
64       chartView_(nullptr),
65       serials_(nullptr),
66       totalSerialsNum_(0),
67       dataSetsRecord_(UNDEFINED),
68       seriesOptions_(nullptr),
69       chartType_(const_cast<char *>(LINE)),
70       headPointStyle_(nullptr),
71       topPointStyle_(nullptr),
72       bottomPointStyle_(nullptr)
73 {
74 }
75 
CreateNativeViews()76 bool ChartComponent::CreateNativeViews()
77 {
78     jerry_value_t attrsPropValue = jerryx_get_property_str(options_, ATTR_ATTRS);
79     jerry_value_t typeStrHandler = jerry_create_string(reinterpret_cast<const jerry_char_t *>(TYPE));
80     jerry_value_t hasTypeProp = jerry_has_property(attrsPropValue, typeStrHandler);
81     if (jerry_get_boolean_value(hasTypeProp)) {
82         jerry_value_t typeValHandler = jerry_get_property(attrsPropValue, typeStrHandler);
83         char *type = MallocStringOf(typeValHandler);
84         jerry_release_value(typeValHandler);
85         if ((type != nullptr) && !strcmp(type, BAR)) {
86             chartView_ = new UIChartPillar();
87             chartType_ = const_cast<char *>(BAR);
88         }
89         ACE_FREE(type);
90     }
91     ReleaseJerryValue(hasTypeProp, typeStrHandler, attrsPropValue, VA_ARG_END_FLAG);
92     if (!strcmp(chartType_, LINE)) {
93         if (chartView_ != nullptr) {
94             delete (chartView_);
95             chartView_ = nullptr;
96         }
97         chartView_ = new UIChartPolyline();
98     }
99     if (chartView_ == nullptr) {
100         HILOG_ERROR(HILOG_MODULE_ACE, "create chart view error");
101         return false;
102     }
103     return Init();
104 }
105 
SetPrivateAttribute(uint16_t attrKeyId,jerry_value_t attrValue)106 bool ChartComponent::SetPrivateAttribute(uint16_t attrKeyId, jerry_value_t attrValue)
107 {
108     if ((chartView_ == nullptr) || !KeyParser::IsKeyValid(attrKeyId) || IS_UNDEFINED(attrValue)) {
109         HILOG_ERROR(HILOG_MODULE_ACE, "chart view has not been created");
110         return false;
111     }
112     bool result = true;
113     switch (attrKeyId) {
114         case K_DATASETS: {
115             dataSetsRecord_ = attrValue;
116             break;
117         }
118         case K_OPTIONS: {
119             result = SetOptions(attrValue);
120             break;
121         }
122         default: {
123             result = false;
124             break;
125         }
126     }
127     return result;
128 }
129 
PostUpdate(uint16_t attrKeyId)130 void ChartComponent::PostUpdate(uint16_t attrKeyId)
131 {
132     UNUSED(attrKeyId);
133     chartView_->ClearDataSerial();
134     chartView_->Invalidate();
135     ClearAllDataSerials();
136     Reset();
137     PostRender();
138 }
139 
PostRender()140 void ChartComponent::PostRender()
141 {
142     uint8_t serialsCount = jerry_get_array_length(dataSetsRecord_);
143     jerry_value_t data = jerry_create_string(reinterpret_cast<const jerry_char_t *>(DATA));
144     UpdateData(dataSetsRecord_, data, serialsCount);
145     jerry_release_value(data);
146     chartView_->Invalidate();
147 }
148 
ReleaseNativeViews()149 void ChartComponent::ReleaseNativeViews()
150 {
151     if (chartView_) {
152         delete (chartView_);
153         chartView_ = nullptr;
154     }
155     ClearAllDataSerials();
156     if (seriesOptions_ != nullptr) {
157         if (seriesOptions_->head) {
158             delete(seriesOptions_->head);
159             seriesOptions_->head = nullptr;
160         }
161         if (seriesOptions_->top) {
162             delete(seriesOptions_->top);
163             seriesOptions_->top = nullptr;
164         }
165         if (seriesOptions_->bottom) {
166             delete(seriesOptions_->bottom);
167             seriesOptions_->bottom = nullptr;
168         }
169         delete(seriesOptions_);
170         seriesOptions_ = nullptr;
171     }
172     if (headPointStyle_ != nullptr) {
173         delete(headPointStyle_);
174         headPointStyle_ = nullptr;
175     }
176     if (topPointStyle_ != nullptr) {
177         delete(topPointStyle_);
178         topPointStyle_ = nullptr;
179     }
180     if (bottomPointStyle_ != nullptr) {
181         delete(bottomPointStyle_);
182         bottomPointStyle_ = nullptr;
183     }
184 }
185 
GetComponentRootView() const186 inline UIView *ChartComponent::GetComponentRootView() const
187 {
188     return chartView_;
189 }
190 
UpdateStrokeColorToSerial(jerry_value_t dataSet,UIChartDataSerial & dataserial)191 void ChartComponent::UpdateStrokeColorToSerial(jerry_value_t dataSet, UIChartDataSerial &dataserial)
192 {
193     if (!strcmp(chartType_, BAR)) {
194         HILOG_WARN(HILOG_MODULE_ACE, "bar not support stroke color set");
195         return;
196     }
197     UpdateColorToSerial(dataSet, dataserial, true);
198 }
199 
UpdateFillColorToSerial(jerry_value_t dataSet,UIChartDataSerial & dataserial)200 void ChartComponent::UpdateFillColorToSerial(jerry_value_t dataSet, UIChartDataSerial& dataserial)
201 {
202     UpdateColorToSerial(dataSet, dataserial, false);
203 }
204 
UpdateStrokeSmoothToSerial(UIChartDataSerial & dataserial)205 void ChartComponent::UpdateStrokeSmoothToSerial(UIChartDataSerial& dataserial)
206 {
207     dataserial.EnableSmooth(seriesOptions_->smooth);
208 }
209 
UpdatePointStyle(UIChartDataSerial::PointStyle * pointStyle,uint32_t fillColor,uint32_t strokeColor,uint32_t radius,uint32_t strokeWidth)210 UIChartDataSerial::PointStyle *ChartComponent::UpdatePointStyle(UIChartDataSerial::PointStyle *pointStyle,
211     uint32_t fillColor, uint32_t strokeColor, uint32_t radius, uint32_t strokeWidth)
212 {
213     if (pointStyle == nullptr) {
214         pointStyle = new UIChartDataSerial::PointStyle();
215         if (pointStyle == nullptr) {
216             return nullptr;
217         }
218     }
219     pointStyle->fillColor = GetRGBColor(fillColor);
220     pointStyle->strokeColor = GetRGBColor(strokeColor);
221     pointStyle->radius = radius;
222     pointStyle->strokeWidth = strokeWidth;
223     return pointStyle;
224 }
225 
UpdatePointToSerial(UIChartDataSerial * dataserial,PointType type)226 bool ChartComponent::UpdatePointToSerial(UIChartDataSerial* dataserial, PointType type)
227 {
228     switch (type) {
229         case PointType::HEAD: {
230             headPointStyle_ = UpdatePointStyle(headPointStyle_, seriesOptions_->head->fillColor,
231                 seriesOptions_->head->strokeColor, seriesOptions_->head->size, seriesOptions_->head->strokeWidth);
232             if (headPointStyle_ == nullptr) {
233                 HILOG_ERROR(HILOG_MODULE_ACE, "malloc headPointStyle heap error");
234                 return false;
235             }
236             dataserial->SetHeadPointStyle(*headPointStyle_);
237             break;
238         }
239         case PointType::TOP: {
240             topPointStyle_ = UpdatePointStyle(topPointStyle_, seriesOptions_->top->fillColor,
241                 seriesOptions_->top->strokeColor, seriesOptions_->top->size, seriesOptions_->top->strokeWidth);
242             if (topPointStyle_ == nullptr) {
243                 HILOG_ERROR(HILOG_MODULE_ACE, "malloc topPointStyle heap error");
244                 return false;
245             }
246             dataserial->SetTopPointStyle(*topPointStyle_);
247             break;
248         }
249         case PointType::BOTTOM: {
250             bottomPointStyle_ = UpdatePointStyle(bottomPointStyle_,
251                 seriesOptions_->bottom->fillColor, seriesOptions_->bottom->strokeColor,
252                 seriesOptions_->bottom->size, seriesOptions_->bottom->strokeWidth);
253             if (bottomPointStyle_ == nullptr) {
254                 HILOG_ERROR(HILOG_MODULE_ACE, "malloc bottomPointStyle heap error");
255                 return false;
256             }
257             dataserial->SetBottomPointStyle(*bottomPointStyle_);
258             break;
259         }
260         default: {
261             break;
262         }
263     }
264     return true;
265 }
266 
UpdatePoints(UIChartDataSerial * dataserial)267 bool ChartComponent::UpdatePoints(UIChartDataSerial* dataserial)
268 {
269     if (seriesOptions_->isTopSet) {
270         if (!UpdatePointToSerial(dataserial, PointType::TOP)) {
271             return false;
272         }
273         dataserial->EnableTopPoint(true);
274     } else {
275         dataserial->EnableTopPoint(false);
276     }
277     if (seriesOptions_->isHeadSet) {
278         if (!UpdatePointToSerial(dataserial, PointType::HEAD)) {
279             return false;
280         }
281         dataserial->EnableHeadPoint(true);
282     } else {
283         dataserial->EnableHeadPoint(false);
284     }
285     if (seriesOptions_->isBottomSet) {
286         if (!UpdatePointToSerial(dataserial, PointType::BOTTOM)) {
287             return false;
288         }
289         dataserial->EnableBottomPoint(true);
290     } else {
291         dataserial->EnableBottomPoint(false);
292     }
293     return true;
294 }
295 
UpdateGradientToSerial(jerry_value_t dataSet,UIChartDataSerial * dataserial)296 void ChartComponent::UpdateGradientToSerial(jerry_value_t dataSet, UIChartDataSerial* dataserial)
297 {
298     jerry_value_t jNeedGradient = jerry_create_string(reinterpret_cast<jerry_char_t *>(const_cast<char *>(GRADIENT)));
299     jerry_value_t hasGradientProperty = jerry_has_property(dataSet, jNeedGradient);
300     if (hasGradientProperty) {
301         bool needGradient = jerry_get_boolean_value(jerry_get_property(dataSet, jNeedGradient));
302         if (!strcmp(chartType_, LINE) && needGradient) {
303             dataserial->EnableGradient(true);
304         }
305     }
306     ReleaseJerryValue(hasGradientProperty, jNeedGradient, VA_ARG_END_FLAG);
307 }
308 
UpdateValuesToSerial(jerry_value_t dataSet,UIChartDataSerial * dataserial,uint16_t dataLen)309 bool ChartComponent::UpdateValuesToSerial(jerry_value_t dataSet, UIChartDataSerial* dataserial, uint16_t dataLen)
310 {
311     bool isSetOk = true;
312     jerry_value_t jData = jerry_create_string(reinterpret_cast<const jerry_char_t *>(DATA));
313     jerry_value_t hasDataProp = jerry_has_property(dataSet, jData);
314     if (hasDataProp) {
315         jerry_value_t jDataValue = jerry_get_property(dataSet, jData);
316         uint16_t len = 0;
317         if (!strcmp(chartType_, BAR)) {
318             len = jerry_get_array_length(jDataValue);
319         } else {
320             if ((xMaxValue_ >= (UINT16_MAX - 1)) || (dataLen < xMinValue_)) {
321                 HILOG_WARN(HILOG_MODULE_ACE, "customMaxCount or customMinValue is set out of boundary");
322             }
323             uint16_t customMaxCount = xMaxValue_ + 1; // the max data count specified by user
324             uint16_t actualMaxCount = dataLen - xMinValue_; // the actual max data count setted in data array
325             len = (customMaxCount < actualMaxCount) ? customMaxCount : actualMaxCount;
326         }
327         if (isSetOk) {
328             Point* pointArray = (len > 0) ? (static_cast<Point *>(ace_malloc(sizeof(Point) * len))) : nullptr;
329             if (pointArray == nullptr) {
330                 HILOG_ERROR(HILOG_MODULE_ACE, "malloc point array memory heap failed");
331                 isSetOk = false;
332             } else {
333                 for (int i = 0; i < len; i++) {
334                     pointArray[i] = { 0, 0 };
335                 }
336                 uint16_t interval = 0;
337                 ParseDataValue(jDataValue, interval, pointArray, len);
338                 dataserial->AddPoints(pointArray, len);
339                 chartView_->AddDataSerial(dataserial);
340                 ace_free(pointArray);
341                 pointArray = nullptr;
342             }
343         }
344         jerry_release_value(jDataValue);
345     } else {
346         HILOG_ERROR(HILOG_MODULE_ACE, "data not set");
347         isSetOk = false;
348     }
349     ReleaseJerryValue(hasDataProp, jData, VA_ARG_END_FLAG);
350     // need to set after datas setted
351     return (isSetOk ? UpdatePoints(dataserial) : false);
352 }
353 
AppendValuesToSerial(jerry_value_t dataSet,UIChartDataSerial * dataserial,uint16_t dataLen)354 bool ChartComponent::AppendValuesToSerial(jerry_value_t dataSet, UIChartDataSerial* dataserial, uint16_t dataLen)
355 {
356     if (dataserial == nullptr) {
357         HILOG_ERROR(HILOG_MODULE_ACE, "dataserial is null");
358         return false;
359     }
360     bool isSetOk = true;
361     jerry_value_t jData = jerry_create_string(reinterpret_cast<const jerry_char_t *>("data"));
362     jerry_value_t hasDataProp = jerry_has_property(dataSet, jData);
363     if (hasDataProp) {
364         jerry_value_t jDataValue = dataSet;
365         uint16_t interval = dataserial->GetDataCount();
366         Point* pointArray = (dataLen > 0) ? (static_cast<Point *>(ace_malloc(sizeof(Point) * dataLen))) : nullptr;
367         if (pointArray == nullptr) {
368             HILOG_ERROR(HILOG_MODULE_ACE, "malloc point array memory heap failed!");
369             isSetOk = false;
370         } else {
371             for (int i = 0; i < dataLen; i++) {
372                 pointArray[i] = { 0, 0 };
373             }
374             ParseDataValue(jDataValue, interval, pointArray, dataLen);
375             AppendValues(*dataserial, pointArray, dataLen);
376             chartView_->RefreshChart();
377             ace_free(pointArray);
378             pointArray = nullptr;
379         }
380     } else {
381         HILOG_ERROR(HILOG_MODULE_ACE, "data not set!");
382         isSetOk = false;
383     }
384     ReleaseJerryValue(hasDataProp, jData, VA_ARG_END_FLAG);
385     return isSetOk;
386 }
387 
AppendValues(UIChartDataSerial & dataserial,Point * pointArray,uint16_t dataLen)388 void ChartComponent::AppendValues(UIChartDataSerial& dataserial, Point* pointArray, uint16_t dataLen)
389 {
390     if (!(seriesOptions_->isLoopSet)) {
391         dataserial.AddPoints(pointArray, dataLen);
392         return;
393     }
394     // the index of the latest updated point (include add or modify)
395     uint16_t latestIndex = dataserial.GetLatestIndex();
396     // the length that will become after adding new data
397     uint16_t expectedDatasLen = latestIndex + dataLen + 1;
398     if ((dataLen == UINT16_MAX) || (UINT16_MAX - latestIndex) < (dataLen + 1)) {
399         HILOG_ERROR(HILOG_MODULE_ACE, "dataLen out of boundary");
400         return;
401     }
402     // the amount of data already in the current data series
403     uint16_t existingDataLen = dataserial.GetDataCount();
404     if (expectedDatasLen > (xMaxValue_ + 1)) {
405         if (latestIndex < existingDataLen - 1) {
406             // means need to modify the value between lastestIndex and existingDataLen
407             for (uint16_t i = 0; i < xMaxValue_ - latestIndex; i++) {
408                 if (i >= dataLen) {
409                     HILOG_ERROR(HILOG_MODULE_ACE, "append data error1");
410                     return;
411                 }
412                 pointArray[i].x = i + latestIndex + 1;
413                 dataserial.ModifyPoint(i + latestIndex + 1, pointArray[i]);
414                 dataserial.HidePoint(i + latestIndex + 1, seriesOptions_->margin);
415             }
416         } else { // means need to add new data to the series
417             dataserial.AddPoints(pointArray, xMaxValue_ - latestIndex);
418         }
419         // Get the data set beyond the screen, and replace the existing data in the sequence one by one
420         // from the beginning
421         chartView_->RefreshChart();
422         for (uint16_t i = 0; i < (expectedDatasLen - (xMaxValue_ + 1)); i++) {
423             if (((xMaxValue_ - latestIndex) + i) >= dataLen) {
424                 HILOG_ERROR(HILOG_MODULE_ACE, "append data error2");
425                 return;
426             }
427             pointArray[(xMaxValue_ - latestIndex) + i].x = i;
428             dataserial.ModifyPoint(i, pointArray[(xMaxValue_ - latestIndex) + i]);
429             dataserial.HidePoint(i, seriesOptions_->margin);
430         }
431     } else { // after adding data, the length will not exceed the maximum value of the x axis
432         if (latestIndex < existingDataLen - 1) {
433             // means need to modify the value between lastestIndex and dataLen
434             for (uint16_t i = 0; i < dataLen; i++) {
435                 pointArray[i].x = latestIndex + 1 + i;
436                 dataserial.ModifyPoint(latestIndex + 1 + i, pointArray[i]);
437                 dataserial.HidePoint(i + latestIndex + 1, seriesOptions_->margin);
438             }
439         } else { // means need to add new data to the series
440             dataserial.AddPoints(pointArray, dataLen);
441         }
442     }
443 }
444 
ParseDataValue(jerry_value_t dataValue,uint16_t interval,Point * pointArray,uint16_t arrayLen)445 bool ChartComponent::ParseDataValue(jerry_value_t dataValue, uint16_t interval, Point* pointArray, uint16_t arrayLen)
446 {
447     for (size_t index = 0; index < arrayLen; index++) {
448         // interval == 0 means update series, otherwise means append data to series
449         jerry_value_t data = (interval == 0) ? jerry_get_property_by_index(dataValue, (index + xMinValue_)) :
450             jerry_get_property_by_index(dataValue, index);
451         if (!jerry_value_is_number(data)) {
452             jerry_release_value(data);
453             HILOG_ERROR(HILOG_MODULE_ACE, "chart data is not a number");
454             return false;
455         }
456         size_t dataValue = static_cast<size_t>(jerry_get_number_value(data));
457         pointArray[index].x = (index + interval);
458         if (dataValue < yMinValue_) {
459             HILOG_WARN(HILOG_MODULE_ACE, "the value %{public}d is less than min value, and will use minValue instead",
460                 dataValue);
461             pointArray[index].y = yMinValue_;
462         } else if (dataValue > yMaxValue_) {
463             HILOG_WARN(HILOG_MODULE_ACE, "the value %{public}d is larger than max value, and will use maxValue instead",
464                 dataValue);
465             pointArray[index].y = yMaxValue_;
466         } else {
467             pointArray[index].y = dataValue;
468         }
469         jerry_release_value(data);
470     }
471     return true;
472 }
473 
AppendDatas(const jerry_value_t func,const jerry_value_t context,const jerry_value_t * args,const jerry_length_t argsNum)474 jerry_value_t ChartComponent::AppendDatas(const jerry_value_t func,
475     const jerry_value_t context,
476     const jerry_value_t *args,
477     const jerry_length_t argsNum)
478 {
479     uint8_t functionParamsNum = 1; // js append function just receive 1 number params
480     if (argsNum != functionParamsNum) {
481         HILOG_ERROR(HILOG_MODULE_ACE, "append function params error");
482     } else {
483         if (jerry_value_is_undefined(args[0])) {
484             HILOG_ERROR(HILOG_MODULE_ACE, "append function params error");
485             return UNDEFINED;
486         }
487         jerry_value_t jAppendDataObj = args[0];
488         uint8_t serialIndex = JerryGetIntegerProperty(jAppendDataObj, APPEND_SERIAL_INDEX);
489         jerry_value_t jDatas = jerryx_get_property_str(jAppendDataObj, DATA);
490         ChartComponent* chartComponent = ChartComponent::GetChartComponent(context);
491         if (chartComponent != nullptr) {
492             DataSerials* tarSerial = const_cast<DataSerials *>(GetDataSerial(chartComponent, serialIndex));
493             if (tarSerial != nullptr) {
494                 chartComponent->AppendData(jDatas, tarSerial);
495             }
496         }
497         jerry_release_value(jDatas);
498     }
499     return UNDEFINED;
500 }
501 
SetOptions(jerry_value_t options)502 bool ChartComponent::SetOptions(jerry_value_t options)
503 {
504     if (!SetOptionsAxis(options, true)) {
505         return false;
506     }
507     if (!SetOptionsAxis(options, false)) {
508         return false;
509     }
510     RecordOptionsSeries(options);
511     return true;
512 }
513 
UpdateData(jerry_value_t dataSets,jerry_value_t keyName,uint8_t serialsCount)514 void ChartComponent::UpdateData(jerry_value_t dataSets, jerry_value_t keyName, uint8_t serialsCount)
515 {
516     if (serials_ != nullptr) {
517         chartView_->ClearDataSerial();
518         ClearAllDataSerials(); // clear all data serials
519     }
520     bool isSetOk = false;
521     for (uint8_t index = 0; index < serialsCount; index++) { // every data set
522         jerry_value_t dataSet = jerry_get_property_by_index(dataSets, index);
523         // check data is set correctly, if the data is not set correctly, need not to set other attribute left
524         jerry_value_t dataValue = jerry_get_property(dataSet, keyName);
525         if ((!jerry_value_is_undefined(dataValue)) && jerry_value_is_array(dataValue)) {
526             uint16_t dataLen = jerry_get_array_length(dataValue);
527             // add to serial linked list, delete all before next update or component finish
528             DataSerials* dataSerialNode = new DataSerials();
529             if (dataSerialNode == nullptr) {
530                 HILOG_ERROR(HILOG_MODULE_ACE, "new dataSerialNode memory heap fail");
531                 ReleaseJerryValue(dataValue, dataSet, VA_ARG_END_FLAG);
532                 break;
533             }
534             AddDataSerial(dataSerialNode);
535             // create dataserial
536             uint16_t dataCount = 0;
537             if (!strcmp(chartType_, BAR)) {
538                 jerry_value_t jData = jerry_create_string(reinterpret_cast<const jerry_char_t *>(DATA));
539                 jerry_value_t jDataValue = jerry_get_property(dataSet, jData);
540                 dataCount = jerry_get_array_length(jDataValue);
541                 ReleaseJerryValue(jDataValue, jData, VA_ARG_END_FLAG);
542             } else {
543                 dataCount = xMaxValue_ + 1;
544             }
545             UIChartDataSerial* dataserial = new UIChartDataSerial();
546             if (dataserial == nullptr) {
547                 HILOG_ERROR(HILOG_MODULE_ACE, "new UIChartDataSerial memory heap fail");
548                 ReleaseJerryValue(dataValue, dataSet, VA_ARG_END_FLAG);
549                 break;
550             }
551             dataserial->SetMaxDataCount(dataCount);
552             dataSerialNode->dataSerial = dataserial;
553             isSetOk = UpdataDataInOrder(dataSet, *dataserial, dataLen);
554         } else {
555             HILOG_ERROR(HILOG_MODULE_ACE, "chart data is not defined or is not array");
556         }
557         ReleaseJerryValue(dataValue, dataSet, VA_ARG_END_FLAG);
558         if (!isSetOk) {
559             break;
560         }
561     }
562 }
563 
UpdataDataInOrder(jerry_value_t dataSet,UIChartDataSerial & dataserial,uint16_t dataLen)564 bool ChartComponent::UpdataDataInOrder(jerry_value_t dataSet, UIChartDataSerial& dataserial, uint16_t dataLen)
565 {
566     UpdateStrokeColorToSerial(dataSet, dataserial);
567     UpdateFillColorToSerial(dataSet, dataserial);
568     UpdateGradientToSerial(dataSet, &dataserial);
569     UpdateStrokeSmoothToSerial(dataserial);
570     return UpdateValuesToSerial(dataSet, &dataserial, dataLen);
571 }
572 
AppendData(jerry_value_t dataSet,DataSerials * dataSerialNode)573 void ChartComponent::AppendData(jerry_value_t dataSet, DataSerials* dataSerialNode)
574 {
575     if (dataSerialNode == nullptr) {
576         HILOG_ERROR(HILOG_MODULE_ACE, "data serial is null");
577         return;
578     }
579     if (!strcmp(chartType_, BAR)) {
580         HILOG_WARN(HILOG_MODULE_ACE, "bar not support append data");
581         return;
582     }
583     uint16_t dataLen = jerry_get_array_length(dataSet);
584     AppendValuesToSerial(dataSet, dataSerialNode->dataSerial, dataLen);
585 }
586 
UpdateColorToSerial(jerry_value_t dataSet,UIChartDataSerial & dataserial,bool isStrokeColor)587 void ChartComponent::UpdateColorToSerial(jerry_value_t dataSet, UIChartDataSerial& dataserial, bool isStrokeColor)
588 {
589     bool isColorSet = false;
590     const char* styleName = isStrokeColor ? STROKE_COLOR : FILL_COLOR;
591     if (jerryx_has_property_str(dataSet, styleName)) {
592         isColorSet = true;
593     } else {
594         // Compatible with old versions (chart1.0)
595         styleName = COMMON_STYLE_BACKGROUND_COLOR;
596         if (jerryx_has_property_str(dataSet, styleName)) {
597             isColorSet = true;
598         }
599     }
600     const uint32_t defaultColorVal = 0xFF6384; // pink
601     uint32_t colorVal = defaultColorVal;
602     if (isColorSet) {
603         colorVal = GetColor(dataSet, const_cast<char *>(styleName), defaultColorVal);
604     }
605     ColorType bgColor = GetRGBColor(colorVal);
606     if (isStrokeColor) {
607         dataserial.SetLineColor(bgColor);
608     } else {
609         dataserial.SetFillColor(bgColor);
610     }
611 }
612 
SetOptionsAxis(jerry_value_t attrValue,bool isXAxis)613 bool ChartComponent::SetOptionsAxis(jerry_value_t attrValue, bool isXAxis)
614 {
615     bool isSetOk = false;
616     jerry_value_t jAxisName = jerry_create_string(reinterpret_cast<const jerry_char_t *>(isXAxis ? X_AXIS : Y_AXIS));
617     jerry_value_t hasXAxisProp = jerry_has_property(attrValue, jAxisName);
618     if (!jerry_get_boolean_value(hasXAxisProp)) {
619         HILOG_ERROR(HILOG_MODULE_ACE, "xAxis and yAxis param must set");
620     } else {
621         jerry_value_t axisValue = jerry_get_property(attrValue, jAxisName);
622         isSetOk = SetOptionsAxisDataRange(axisValue, isXAxis);
623         if (isSetOk) {
624             SetOptionsAxisTick(axisValue, isXAxis);
625             isSetOk = SetOptionsAxisDisplay(axisValue, isXAxis);
626         }
627         if (isSetOk) {
628             SetOptionsAxisColor(axisValue, isXAxis);
629         }
630         jerry_release_value(axisValue);
631     }
632     ReleaseJerryValue(jAxisName, hasXAxisProp, VA_ARG_END_FLAG);
633     return isSetOk;
634 }
635 
SetOptionsAxisDataRange(jerry_value_t xAxisValue,bool isXAxis)636 bool ChartComponent::SetOptionsAxisDataRange(jerry_value_t xAxisValue, bool isXAxis)
637 {
638     bool isSetOk = true;
639     const uint8_t defaultMinValue = 0;
640     const uint8_t defaultMaxValue = 100;
641     jerry_value_t jMinValue = jerryx_get_property_str(xAxisValue, MIN);
642     jerry_value_t jMaxValue = jerryx_get_property_str(xAxisValue, MAX);
643     double tempMinVal = jerry_get_number_value(jMinValue);
644     double tempMaxVal = jerry_get_number_value(jMaxValue);
645     uint16_t minValue = defaultMinValue;
646     uint16_t maxValue = defaultMaxValue;
647     if ((tempMinVal >= 0) && (tempMinVal <= UINT16_MAX)) {
648         minValue = static_cast<uint16_t>(tempMinVal);
649     }
650     if ((tempMaxVal >= 0) && (tempMaxVal <= UINT16_MAX)) {
651         maxValue = static_cast<uint16_t>(tempMaxVal);
652     }
653 
654     if (maxValue == 0) {
655         if (!jerryx_has_property_str(xAxisValue, "max")) {
656             HILOG_WARN(HILOG_MODULE_ACE,
657                 "max value is not defined, and will use default value %{public}d instead", defaultMaxValue);
658             maxValue = defaultMaxValue;
659         }
660     }
661     if (isXAxis) {
662         if (!strcmp(chartType_, BAR)) {
663             xAxis_->SetDataRange(0, 0);
664         } else {
665             isSetOk = SetOptionsAxisDataRange(minValue, maxValue, defaultMinValue, defaultMaxValue, true);
666         }
667     } else {
668         isSetOk = SetOptionsAxisDataRange(minValue, maxValue, defaultMinValue, defaultMaxValue, false);
669     }
670     ReleaseJerryValue(jMinValue, jMaxValue, VA_ARG_END_FLAG);
671     return isSetOk;
672 }
673 
SetOptionsAxisDataRange(uint16_t minValue,uint16_t maxValue,uint8_t defaultMinValue,uint8_t defaultMaxValue,bool isXaxis)674 bool ChartComponent::SetOptionsAxisDataRange(uint16_t minValue, uint16_t maxValue, uint8_t defaultMinValue,
675     uint8_t defaultMaxValue, bool isXaxis)
676 {
677     bool isSetOk = true;
678     uint8_t supportMinValue = 0;
679     uint16_t supportedMaxValue = 65535;
680     const char * const xOrY = isXaxis ?  "x" : "y";
681     if ((minValue < supportMinValue) || (minValue > supportedMaxValue)) {
682         HILOG_WARN(HILOG_MODULE_ACE,
683                    "%{public}s min value: %{public}d is not between %{public}d ~ %{public}d, and will use default "
684                    "value %{public}d instead",
685             xOrY, minValue, supportMinValue, supportedMaxValue, defaultMinValue);
686         minValue = defaultMinValue;
687     }
688     if ((maxValue < supportMinValue) || (maxValue > supportedMaxValue)) {
689         HILOG_WARN(HILOG_MODULE_ACE,
690                    "%{public}s max value: %{public}d is not between %{public}d ~ %{public}d, and will use default "
691                    "value %{public}d instead",
692             xOrY, maxValue, supportMinValue, supportedMaxValue, defaultMaxValue);
693         maxValue = defaultMaxValue;
694     }
695     if (minValue > maxValue) {
696         HILOG_WARN(HILOG_MODULE_ACE, "%{public}s min value is larger than max value, we will use default value", xOrY);
697         maxValue = defaultMaxValue;
698         minValue = defaultMinValue;
699     }
700     if (isXaxis) {
701         xAxis_->SetDataRange(0, maxValue - minValue);
702         xMaxValue_ = maxValue - minValue;
703         xMinValue_ = minValue;
704     } else {
705         yAxis_->SetDataRange(minValue, maxValue);
706         yMinValue_ = minValue;
707         yMaxValue_ = maxValue;
708     }
709     return isSetOk;
710 }
711 
SetOptionsAxisTick(jerry_value_t xAxisValue,bool isXAxis)712 void ChartComponent::SetOptionsAxisTick(jerry_value_t xAxisValue, bool isXAxis)
713 {
714     jerry_value_t jAxisTickValue = jerryx_get_property_str(xAxisValue, "axisTick");
715     double tempAxisTickVal = jerry_get_number_value(jAxisTickValue);
716     int16_t axisTickValue;
717     const int8_t minTickValue = 1;
718     const int8_t maxTickValue = 20;
719     if ((tempAxisTickVal < minTickValue) || (tempAxisTickVal > maxTickValue)) {
720         int16_t defaultValue = 10; // default value, means total number of ticks
721         axisTickValue = defaultValue;
722     } else {
723         axisTickValue = static_cast<int16_t>(tempAxisTickVal);
724     }
725     jerry_release_value(jAxisTickValue);
726     if (isXAxis) {
727         xAxis_->SetMarkNum(axisTickValue);
728     } else {
729         yAxis_->SetMarkNum(axisTickValue);
730     }
731 }
732 
SetOptionsAxisDisplay(jerry_value_t xAxisValue,bool isXAxis)733 bool ChartComponent::SetOptionsAxisDisplay(jerry_value_t xAxisValue, bool isXAxis)
734 {
735     bool displayValue = false; // default value, means not show axis
736     jerry_value_t jDisplayValue = jerryx_get_property_str(xAxisValue, DISPLAY);
737     if (!jerry_value_is_undefined(jDisplayValue)) {
738         displayValue = jerry_get_boolean_value(jDisplayValue);
739     }
740     if (isXAxis) {
741         xAxis_->SetVisible(displayValue);
742     } else {
743         yAxis_->SetVisible(displayValue);
744     }
745     jerry_release_value(jDisplayValue);
746     return true;
747 }
748 
SetOptionsAxisColor(jerry_value_t xAxisValue,bool isXAxis)749 void ChartComponent::SetOptionsAxisColor(jerry_value_t xAxisValue, bool isXAxis)
750 {
751     const char * const styleName = COLOR;
752     uint32_t color = 0xc0c0c0; // default color
753     if (jerryx_has_property_str(xAxisValue, styleName)) {
754         uint16_t len = 0;
755         char* styleValue = JerryMallocStringProperty(xAxisValue, styleName, len);
756         uint8_t alpha = 0;
757         (void)ParseColor(styleValue, color, alpha);
758         ACE_FREE(styleValue);
759     }
760     ColorType axisColor = GetRGBColor(color);
761     if (isXAxis) {
762         xAxis_->SetLineColor(axisColor);
763     } else {
764         yAxis_->SetLineColor(axisColor);
765     }
766 }
767 
RecordOptionsSeries(jerry_value_t options)768 void ChartComponent::RecordOptionsSeries(jerry_value_t options)
769 {
770     if (jerryx_has_property_str(options, SERIES)) {
771         jerry_value_t jSeriesVal = jerryx_get_property_str(options, SERIES);
772         RecordOptionsSeriesLineStyle(jSeriesVal);
773         RecordOptionsSeriesPoint(jSeriesVal, PointType::HEAD);
774         RecordOptionsSeriesPoint(jSeriesVal, PointType::TOP);
775         RecordOptionsSeriesPoint(jSeriesVal, PointType::BOTTOM);
776         RecordOptionsSeriesLoop(jSeriesVal);
777         jerry_release_value(jSeriesVal);
778     }
779 }
780 
RecordOptionsSeriesLineStyle(jerry_value_t jSeriesVal)781 void ChartComponent::RecordOptionsSeriesLineStyle(jerry_value_t jSeriesVal)
782 {
783     char* styleName = const_cast<char *>(LINE_STYLE);
784     if (!jerryx_has_property_str(jSeriesVal, styleName)) {
785         return;
786     }
787     jerry_value_t jLineStyleVal = jerryx_get_property_str(jSeriesVal, styleName);
788     styleName = const_cast<char *>(WIDTH);
789     uint16_t widthVal = GetSizeVal(jLineStyleVal, styleName, 1);
790     chartView_->SetStyle(STYLE_LINE_WIDTH, widthVal); // chart width set can affect all the series
791     seriesOptions_->width = widthVal;
792     styleName = const_cast<char *>(SMOOTH);
793     if (jerryx_has_property_str(jLineStyleVal, styleName)) {
794         jerry_value_t jSmoothVal = jerryx_get_property_str(jLineStyleVal, styleName);
795         seriesOptions_->smooth = jerry_get_boolean_value(jSmoothVal);
796         jerry_release_value(jSmoothVal);
797     }
798     jerry_release_value(jLineStyleVal);
799 }
800 
RecordOptionsSeriesPoint(jerry_value_t jSeriesVal,PointType type)801 void ChartComponent::RecordOptionsSeriesPoint(jerry_value_t jSeriesVal, PointType type)
802 {
803     char* styleName = nullptr;
804     switch (type) {
805         case PointType::HEAD: {
806             styleName = const_cast<char *>(HEAD_POINT);
807             break;
808         }
809         case PointType::TOP: {
810             styleName = const_cast<char *>(TOP_POINT);
811             break;
812         }
813         case PointType::BOTTOM: {
814             styleName = const_cast<char *>(BOTTOM_POINT);
815             break;
816         }
817         default: {
818             break;
819         }
820     }
821     if ((styleName == nullptr) || !jerryx_has_property_str(jSeriesVal, styleName)) {
822         return;
823     }
824     SetOptionsInfo(jSeriesVal, type, styleName);
825 }
826 
SetOptionsInfo(jerry_value_t jSeriesVal,PointType type,char * styleName)827 void ChartComponent::SetOptionsInfo(jerry_value_t jSeriesVal, PointType type, char* styleName)
828 {
829     jerry_value_t jPointStyleVal = jerryx_get_property_str(jSeriesVal, styleName);
830     styleName = const_cast<char *>(SIZE);
831     uint8_t defaultValue = 4;
832     uint8_t size = GetSizeVal(jPointStyleVal, styleName, defaultValue);
833     uint32_t defaultColorVal = 0xff0000;
834     styleName = const_cast<char *>(STROKE_COLOR);
835     uint32_t strokeColor = GetColor(jPointStyleVal, styleName, defaultColorVal);
836     defaultValue = 1;
837     styleName = const_cast<char *>(STROKE_WIDTH);
838     uint8_t strokeWidth = GetSizeVal(jPointStyleVal, styleName, defaultValue);
839     styleName = const_cast<char *>(FILL_COLOR);
840     uint32_t fillColor = GetColor(jPointStyleVal, styleName, defaultColorVal);
841     bool isDisplay = GetDisplayStatus(jPointStyleVal);
842 
843     jerry_release_value(jPointStyleVal);
844     switch (type) {
845         case PointType::HEAD: {
846             seriesOptions_->isHeadSet = isDisplay;
847             seriesOptions_->head->size = size;
848             seriesOptions_->head->strokeColor = strokeColor;
849             seriesOptions_->head->fillColor = fillColor;
850             seriesOptions_->head->strokeWidth = strokeWidth;
851             break;
852         }
853         case PointType::TOP: {
854             seriesOptions_->isTopSet = isDisplay;
855             seriesOptions_->top->size = size;
856             seriesOptions_->top->strokeColor = strokeColor;
857             seriesOptions_->top->fillColor = fillColor;
858             seriesOptions_->top->strokeWidth = strokeWidth;
859             break;
860         }
861         case PointType::BOTTOM: {
862             seriesOptions_->isBottomSet = isDisplay;
863             seriesOptions_->bottom->size = size;
864             seriesOptions_->bottom->strokeColor = strokeColor;
865             seriesOptions_->bottom->fillColor = fillColor;
866             seriesOptions_->bottom->strokeWidth = strokeWidth;
867             break;
868         }
869         default: {
870             break;
871         }
872     }
873 }
874 
RecordOptionsSeriesLoop(jerry_value_t jSeriesVal)875 void ChartComponent::RecordOptionsSeriesLoop(jerry_value_t jSeriesVal)
876 {
877     char* styleName = const_cast<char *>(LOOP);
878     if (!jerryx_has_property_str(jSeriesVal, styleName)) {
879         return;
880     }
881 
882     jerry_value_t jLoopStyleVal = jerryx_get_property_str(jSeriesVal, styleName);
883     seriesOptions_->isLoopSet = GetDisplayStatus(jLoopStyleVal);
884 
885     styleName = const_cast<char *>(MARGIN);
886     if (!jerryx_has_property_str(jLoopStyleVal, styleName)) {
887         seriesOptions_->margin = 1;
888     } else {
889         int16_t marginVal = JerryGetIntegerProperty(jLoopStyleVal, styleName);
890         seriesOptions_->margin = marginVal;
891     }
892     jerry_release_value(jLoopStyleVal);
893 }
894 
Reset()895 inline void ChartComponent::Reset()
896 {
897     totalSerialsNum_ = 0;
898 }
899 
AddDataSerial(DataSerials * serial)900 void ChartComponent::AddDataSerial(DataSerials *serial)
901 {
902     if (serial == nullptr) {
903         return;
904     }
905     if (serials_) {
906         serial->next = serials_;
907         totalSerialsNum_++;
908     } else {
909         totalSerialsNum_ = 1;
910     }
911     serials_ = serial;
912 }
913 
GetChartComponent(jerry_value_t nativeElement)914 ChartComponent* ChartComponent::GetChartComponent(jerry_value_t nativeElement)
915 {
916     ChartComponent* chartComponent =
917         reinterpret_cast<ChartComponent *>(ComponentUtils::GetComponentFromBindingObject(nativeElement));
918     if (chartComponent == nullptr) {
919         HILOG_ERROR(HILOG_MODULE_ACE, "get binded component fail");
920     }
921     return chartComponent;
922 }
923 
GetDataSerial(ChartComponent * chartComponent,uint8_t index)924 const DataSerials* ChartComponent::GetDataSerial(ChartComponent* chartComponent, uint8_t index)
925 {
926     if (chartComponent == nullptr) {
927         HILOG_ERROR(HILOG_MODULE_ACE, "chartComponent is null");
928         return nullptr;
929     }
930     uint8_t targetSerial = index + 1;
931     if (targetSerial > chartComponent->totalSerialsNum_) {
932         HILOG_ERROR(HILOG_MODULE_ACE, "data serial index should not larger than total serials num - 1");
933         return nullptr;
934     }
935     targetSerial = chartComponent->totalSerialsNum_ - targetSerial + 1; // serials_ is added reversed before
936     DataSerials* serial = chartComponent->serials_;
937     while (targetSerial > 1) {
938         serial = serial->next;
939         targetSerial--;
940     }
941     return serial;
942 }
943 
ClearDataSerialsNode(DataSerials * serial)944 void ChartComponent::ClearDataSerialsNode(DataSerials* serial)
945 {
946     if (serial == nullptr) {
947         return;
948     }
949     if (serial->next) {
950         ClearDataSerialsNode(serial->next);
951     }
952     if (serial->dataSerial) {
953         delete (serial->dataSerial);
954         serial->dataSerial = nullptr;
955     }
956     delete (serial);
957     serial = nullptr;
958 }
959 
ClearAllDataSerials()960 void ChartComponent::ClearAllDataSerials()
961 {
962     ClearDataSerialsNode(serials_);
963     serials_ = nullptr;
964 }
965 
GetSizeVal(jerry_value_t obj,const char * const styleName,uint16_t defaultValue)966 uint16_t ChartComponent::GetSizeVal(jerry_value_t obj, const char * const styleName, uint16_t defaultValue)
967 {
968     if (jerryx_has_property_str(obj, styleName)) {
969         uint16_t len = 0;
970         char* sizeVal = JerryMallocStringProperty(obj, styleName, len);
971         if (sizeVal == nullptr) {
972             HILOG_WARN(HILOG_MODULE_ACE, "Get Style String value failed, return default value!");
973             return defaultValue;
974         }
975         uint16_t size = strtol(sizeVal, nullptr, DEC);
976         ace_free(sizeVal);
977         sizeVal = nullptr;
978         return size;
979     }
980     return defaultValue;
981 }
982 
GetColor(jerry_value_t obj,const char * const styleName,uint32_t defaultValue)983 uint32_t ChartComponent::GetColor(jerry_value_t obj, const char * const styleName, uint32_t defaultValue)
984 {
985     uint8_t alpha = 0;
986     uint32_t color = defaultValue;
987     uint16_t len = 0;
988     char* styleValue = JerryMallocStringProperty(obj, styleName, len);
989     if (!ParseColor(styleValue, color, alpha)) {
990         HILOG_WARN(HILOG_MODULE_ACE, "color format error");
991     }
992     ACE_FREE(styleValue);
993     return color;
994 }
995 
GetDisplayStatus(jerry_value_t obj)996 bool ChartComponent::GetDisplayStatus(jerry_value_t obj)
997 {
998     if (!jerryx_has_property_str(obj, DISPLAY)) {
999         return true;
1000     }
1001     jerry_value_t jDisplayValue = jerryx_get_property_str(obj, DISPLAY);
1002     return jerry_get_boolean_value(jDisplayValue);
1003 }
1004 
Init()1005 bool ChartComponent::Init()
1006 {
1007     seriesOptions_ = new SeriesOptions();
1008     if (seriesOptions_ == nullptr) {
1009         HILOG_ERROR(HILOG_MODULE_ACE, "malloc seriesOptions heap error");
1010         return false;
1011     }
1012     seriesOptions_->head = new PointOptions();
1013     if (seriesOptions_->head == nullptr) {
1014         HILOG_ERROR(HILOG_MODULE_ACE, "malloc PointOptions head heap error");
1015         return false;
1016     }
1017     seriesOptions_->top = new PointOptions();
1018     if (seriesOptions_->top == nullptr) {
1019         HILOG_ERROR(HILOG_MODULE_ACE, "malloc PointOptions top heap error");
1020         return false;
1021     }
1022     seriesOptions_->bottom = new PointOptions();
1023     if (seriesOptions_->bottom == nullptr) {
1024         HILOG_ERROR(HILOG_MODULE_ACE, "malloc PointOptions bottom heap error");
1025         return false;
1026     }
1027 
1028     xAxis_ = &(chartView_->GetXAxis());
1029     yAxis_ = &(chartView_->GetYAxis());
1030     if ((xAxis_ == nullptr) || (yAxis_ == nullptr)) {
1031         HILOG_ERROR(HILOG_MODULE_ACE, "get axis error");
1032         return false;
1033     }
1034     const int8_t originPosition = 0;
1035     chartView_->SetPosition(originPosition, originPosition);
1036     chartView_->SetStyle(StyleDefault::GetBrightStyle());
1037     chartView_->SetStyle(STYLE_BACKGROUND_OPA, 0);
1038     chartView_->SetStyle(STYLE_BORDER_OPA, 0);
1039     chartView_->SetStyle(STYLE_BORDER_WIDTH, 0);
1040     chartView_->SetStyle(STYLE_PADDING_LEFT, 0);
1041     RegisterNamedFunction(FUNC_JS_API, AppendDatas);
1042     return true;
1043 }
1044 } // namespace ACELite
1045 } // namespace OHOS
1046