• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 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
16import ts from 'typescript';
17
18import {
19  INNER_COMPONENT_MEMBER_DECORATORS,
20  COMPONENT_NON_DECORATOR,
21  COMPONENT_STATE_DECORATOR,
22  COMPONENT_PROP_DECORATOR,
23  COMPONENT_LINK_DECORATOR,
24  COMPONENT_STORAGE_PROP_DECORATOR,
25  COMPONENT_STORAGE_LINK_DECORATOR,
26  COMPONENT_PROVIDE_DECORATOR,
27  COMPONENT_CONSUME_DECORATOR,
28  COMPONENT_OBJECT_LINK_DECORATOR,
29  COMPONENT_WATCH_DECORATOR,
30  COMPONENT_OBSERVED_DECORATOR,
31  OBSERVED_PROPERTY_SIMPLE,
32  OBSERVED_PROPERTY_OBJECT,
33  SYNCHED_PROPERTY_SIMPLE_ONE_WAY,
34  SYNCHED_PROPERTY_SIMPLE_TWO_WAY,
35  SYNCHED_PROPERTY_OBJECT_TWO_WAY,
36  SYNCHED_PROPERTY_NESED_OBJECT,
37  CREATE_GET_METHOD,
38  CREATE_SET_METHOD,
39  CREATE_NEWVALUE_IDENTIFIER,
40  CREATE_CONSTRUCTOR_PARAMS,
41  ADD_PROVIDED_VAR,
42  INITIALIZE_CONSUME_FUNCTION,
43  APP_STORAGE,
44  APP_STORAGE_SET_AND_PROP,
45  APP_STORAGE_SET_AND_LINK,
46  APP_STORAGE_GET_OR_SET,
47  COMPONENT_CONSTRUCTOR_UNDEFINED,
48  SET_CONTROLLER_METHOD,
49  SET_CONTROLLER_CTR,
50  SET_CONTROLLER_CTR_TYPE,
51  BASE_COMPONENT_NAME,
52  COMPONENT_CREATE_FUNCTION,
53  COMPONENT_BUILDERPARAM_DECORATOR,
54  COMPONENT_LOCAL_STORAGE_LINK_DECORATOR,
55  COMPONENT_LOCAL_STORAGE_PROP_DECORATOR,
56  COMPONENT_CUSTOM_DECORATOR
57} from './pre_define';
58import {
59  forbiddenUseStateType,
60  BUILDIN_STYLE_NAMES
61} from './component_map';
62import {
63  observedClassCollection,
64  enumCollection,
65  componentCollection,
66  classMethodCollection
67} from './validate_ui_syntax';
68import { updateConstructor } from './process_component_constructor';
69import {
70  LogType,
71  LogInfo,
72  componentInfo,
73  createFunction
74} from './utils';
75import {
76  createReference,
77  isProperty
78} from './process_component_class';
79import { globalProgram } from '../main';
80
81export type ControllerType = {
82  hasController: boolean
83}
84
85export const observedPropertyDecorators: Set<string> =
86  new Set([COMPONENT_STATE_DECORATOR, COMPONENT_PROVIDE_DECORATOR]);
87
88export const propAndLinkDecorators: Set<string> =
89  new Set([COMPONENT_PROP_DECORATOR, COMPONENT_LINK_DECORATOR]);
90
91export const appStorageDecorators: Set<string> =
92  new Set([COMPONENT_STORAGE_PROP_DECORATOR, COMPONENT_STORAGE_LINK_DECORATOR,
93    COMPONENT_LOCAL_STORAGE_LINK_DECORATOR, COMPONENT_LOCAL_STORAGE_PROP_DECORATOR]);
94
95export const mandatorySpecifyDefaultValueDecorators: Set<string> =
96  new Set([...observedPropertyDecorators, ...appStorageDecorators]);
97
98export const forbiddenSpecifyDefaultValueDecorators: Set<string> =
99  new Set([...propAndLinkDecorators, COMPONENT_CONSUME_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR]);
100
101export const mandatoryToInitViaParamDecorators: Set<string> =
102  new Set([...propAndLinkDecorators, COMPONENT_OBJECT_LINK_DECORATOR]);
103
104export const setUpdateParamsDecorators: Set<string> =
105  new Set([...observedPropertyDecorators, COMPONENT_PROP_DECORATOR, COMPONENT_OBJECT_LINK_DECORATOR,
106    COMPONENT_BUILDERPARAM_DECORATOR
107  ]);
108
109export const immutableDecorators: Set<string> =
110  new Set([COMPONENT_OBJECT_LINK_DECORATOR, COMPONENT_BUILDERPARAM_DECORATOR]);
111
112export const simpleTypes: Set<ts.SyntaxKind> = new Set([ts.SyntaxKind.StringKeyword,
113  ts.SyntaxKind.NumberKeyword, ts.SyntaxKind.BooleanKeyword, ts.SyntaxKind.EnumDeclaration]);
114
115export const decoratorParamSet: Set<string> = new Set();
116
117export const stateObjectCollection: Set<string> = new Set();
118
119export const builderParamObjectCollection: Map<string, Set<string>> = new Map();
120
121export class UpdateResult {
122  private itemUpdate: boolean = false;
123  private ctorUpdate: boolean = false;
124  private properity: ts.PropertyDeclaration;
125  private ctor: ts.ConstructorDeclaration;
126  private variableGet: ts.GetAccessorDeclaration;
127  private variableSet: ts.SetAccessorDeclaration;
128  private updateParams: ts.Statement;
129  private deleteParams: boolean = false;
130  private controllerSet: ts.MethodDeclaration;
131
132  public setProperity(updateItem: ts.PropertyDeclaration) {
133    this.itemUpdate = true;
134    this.properity = updateItem;
135  }
136
137  public setCtor(updateCtor: ts.ConstructorDeclaration) {
138    this.ctorUpdate = true;
139    this.ctor = updateCtor;
140  }
141
142  public setControllerSet(updateControllerSet: ts.MethodDeclaration) {
143    this.controllerSet = updateControllerSet;
144  }
145
146  public getControllerSet(): ts.MethodDeclaration {
147    return this.controllerSet;
148  }
149
150  public setVariableGet(updateVariableGet: ts.GetAccessorDeclaration) {
151    this.variableGet = updateVariableGet;
152  }
153
154  public setVariableSet(updateVariableSet: ts.SetAccessorDeclaration) {
155    this.variableSet = updateVariableSet;
156  }
157
158  public setUpdateParams(updateParams: ts.Statement) {
159    this.updateParams = updateParams;
160  }
161
162  public setDeleteParams(deleteParams: boolean) {
163    this.deleteParams = deleteParams;
164  }
165
166  public isItemUpdate(): boolean {
167    return this.itemUpdate;
168  }
169
170  public isCtorUpdate(): boolean {
171    return this.ctorUpdate;
172  }
173
174  public getProperity(): ts.PropertyDeclaration {
175    return this.properity;
176  }
177
178  public getCtor(): ts.ConstructorDeclaration {
179    return this.ctor;
180  }
181
182  public getUpdateParams(): ts.Statement {
183    return this.updateParams;
184  }
185
186  public getVariableGet(): ts.GetAccessorDeclaration {
187    return this.variableGet;
188  }
189
190  public getVariableSet(): ts.SetAccessorDeclaration {
191    return this.variableSet;
192  }
193
194  public isDeleteParams(): boolean {
195    return this.deleteParams;
196  }
197}
198
199export const curPropMap: Map<string, string> = new Map();
200
201export function processMemberVariableDecorators(parentName: ts.Identifier,
202  item: ts.PropertyDeclaration, ctorNode: ts.ConstructorDeclaration, watchMap: Map<string, ts.Node>,
203  checkController: ControllerType, log: LogInfo[], program: ts.Program, context: ts.TransformationContext,
204  hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration): UpdateResult {
205  const updateResult: UpdateResult = new UpdateResult();
206  const name: ts.Identifier = item.name as ts.Identifier;
207  if (!item.decorators || !item.decorators.length) {
208    curPropMap.set(name.escapedText.toString(), COMPONENT_NON_DECORATOR);
209    updateResult.setProperity(undefined);
210    updateResult.setUpdateParams(createUpdateParams(name, COMPONENT_NON_DECORATOR));
211    updateResult.setCtor(updateConstructor(ctorNode, [], [
212      createVariableInitStatement(item, COMPONENT_NON_DECORATOR, log, program, context, hasPreview,
213        interfaceNode)]));
214    updateResult.setControllerSet(createControllerSet(item, parentName, name, checkController));
215  } else if (!item.type) {
216    validatePropertyNonType(name, log);
217    return updateResult;
218  } else if (validateCustomDecorator(item.decorators, log)) {
219    updateResult.setUpdateParams(createUpdateParams(name, COMPONENT_CUSTOM_DECORATOR));
220  } else {
221    processPropertyNodeDecorator(parentName, item, updateResult, ctorNode, name, watchMap,
222      log, program, context, hasPreview, interfaceNode);
223  }
224  return updateResult;
225}
226
227function createControllerSet(node: ts.PropertyDeclaration, componentName: ts.Identifier,
228  name: ts.Identifier, checkController: ControllerType): ts.MethodDeclaration {
229  if (componentCollection.customDialogs.has(componentName.getText()) && node.type &&
230    node.type.getText() === SET_CONTROLLER_CTR_TYPE) {
231    checkController.hasController = true;
232    return ts.factory.createMethodDeclaration(undefined, undefined, undefined,
233      ts.factory.createIdentifier(SET_CONTROLLER_METHOD), undefined, undefined,
234      [ts.factory.createParameterDeclaration(undefined, undefined, undefined,
235        ts.factory.createIdentifier(SET_CONTROLLER_CTR), undefined,
236        ts.factory.createTypeReferenceNode(ts.factory.createIdentifier(SET_CONTROLLER_CTR_TYPE),
237          undefined), undefined)], undefined, ts.factory.createBlock(
238        [ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
239          ts.factory.createPropertyAccessExpression(ts.factory.createThis(), name),
240          ts.factory.createToken(ts.SyntaxKind.EqualsToken),
241          ts.factory.createIdentifier(SET_CONTROLLER_CTR)))], true));
242  }
243}
244
245function processPropertyNodeDecorator(parentName: ts.Identifier, node: ts.PropertyDeclaration,
246  updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, name: ts.Identifier,
247  watchMap: Map<string, ts.Node>, log: LogInfo[], program: ts.Program,
248  context: ts.TransformationContext, hasPreview: boolean, interfaceNode: ts.InterfaceDeclaration):
249  void {
250  let stateManagementDecoratorCount: number = 0;
251  for (let i = 0; i < node.decorators.length; i++) {
252    const decoratorName: string = node.decorators[i].getText().replace(/\(.*\)$/, '').trim();
253    if (decoratorName !== COMPONENT_WATCH_DECORATOR) {
254      curPropMap.set(name.escapedText.toString(), decoratorName);
255    }
256    if (BUILDIN_STYLE_NAMES.has(decoratorName.replace('@', ''))) {
257      validateDuplicateDecorator(node.decorators[i], log);
258    }
259    if (decoratorName !== COMPONENT_WATCH_DECORATOR && isForbiddenUseStateType(node.type)) {
260      // @ts-ignore
261      validateForbiddenUseStateType(name, decoratorName, node.type.typeName.getText(), log);
262      return;
263    }
264    if (parentName.getText() === componentCollection.entryComponent &&
265      mandatoryToInitViaParamDecorators.has(decoratorName)) {
266      validateHasIllegalDecoratorInEntry(parentName, name, decoratorName, log);
267    }
268    if (node.initializer && forbiddenSpecifyDefaultValueDecorators.has(decoratorName)) {
269      validatePropertyDefaultValue(name, decoratorName, log);
270      return;
271    } else if (!node.initializer && mandatorySpecifyDefaultValueDecorators.has(decoratorName)) {
272      validatePropertyNonDefaultValue(name, decoratorName, log);
273      return;
274    }
275    if (node.questionToken && mandatoryToInitViaParamDecorators.has(decoratorName)) {
276      validateHasIllegalQuestionToken(name, decoratorName, log);
277    }
278    if (!isSimpleType(node.type, program) &&
279      decoratorName !== COMPONENT_BUILDERPARAM_DECORATOR) {
280      stateObjectCollection.add(name.escapedText.toString());
281    }
282    if (decoratorName === COMPONENT_WATCH_DECORATOR &&
283      validateWatchDecorator(name, node.decorators.length, log)) {
284      processWatch(node, node.decorators[i], watchMap, log);
285    } else if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
286      stateManagementDecoratorCount += 1;
287      processStateDecorators(node, decoratorName, updateResult, ctorNode, log, program, context,
288        hasPreview, interfaceNode);
289    }
290  }
291  if (stateManagementDecoratorCount > 1) {
292    validateMultiDecorators(name, log);
293    return;
294  }
295}
296
297function processStateDecorators(node: ts.PropertyDeclaration, decorator: string,
298  updateResult: UpdateResult, ctorNode: ts.ConstructorDeclaration, log: LogInfo[],
299  program: ts.Program, context: ts.TransformationContext, hasPreview:boolean,
300  interfaceNode: ts.InterfaceDeclaration): void {
301  const name: ts.Identifier = node.name as ts.Identifier;
302  updateResult.setProperity(undefined);
303  const updateState: ts.Statement[] = [];
304  const variableInitStatement: ts.Statement =
305    createVariableInitStatement(node, decorator, log, program, context, hasPreview, interfaceNode);
306  if (variableInitStatement) {
307    updateState.push(variableInitStatement);
308  }
309  addAddProvidedVar(node, name, decorator, updateState);
310  updateResult.setCtor(updateConstructor(ctorNode, [], [...updateState], false));
311  if (decorator !== COMPONENT_BUILDERPARAM_DECORATOR) {
312    updateResult.setVariableGet(createGetAccessor(name, CREATE_GET_METHOD));
313    updateResult.setDeleteParams(true);
314  }
315  if (!immutableDecorators.has(decorator)) {
316    updateResult.setVariableSet(createSetAccessor(name, CREATE_SET_METHOD, node.type));
317  }
318  if (setUpdateParamsDecorators.has(decorator)) {
319    updateResult.setUpdateParams(createUpdateParams(name, decorator));
320  }
321}
322
323function processWatch(node: ts.PropertyDeclaration, decorator: ts.Decorator,
324  watchMap: Map<string, ts.Node>, log: LogInfo[]): void {
325  if (node.name) {
326    const propertyName: string = node.name.getText();
327    if (decorator.expression && ts.isCallExpression(decorator.expression) &&
328      decorator.expression.arguments && decorator.expression.arguments.length === 1) {
329      const currentClassMethod: Set<string> = classMethodCollection.get(node.parent.name.getText());
330      const argument: ts.Node = decorator.expression.arguments[0];
331      if (ts.isStringLiteral(argument)) {
332        if (currentClassMethod.has(argument.text)) {
333          watchMap.set(propertyName, argument);
334        } else {
335          log.push({
336            type: LogType.ERROR,
337            message: `Cannot find name ${argument.getText()} in struct '${node.parent.name.getText()}'.`,
338            pos: argument.getStart()
339          });
340        }
341      } else if (ts.isIdentifier(decorator.expression.arguments[0])) {
342        const content: string = decorator.expression.arguments[0].getText();
343        const propertyNode: ts.PropertyAccessExpression = createPropertyAccessExpressionWithThis(content);
344        watchMap.set(propertyName, propertyNode);
345        decoratorParamSet.add(content);
346        validateWatchParam(LogType.WARN, argument.getStart(), log);
347      } else if (ts.isPropertyAccessExpression(decorator.expression.arguments[0])) {
348        watchMap.set(propertyName, decorator.expression.arguments[0]);
349        validateWatchParam(LogType.WARN, argument.getStart(), log);
350      } else {
351        validateWatchParam(LogType.ERROR, argument.getStart(), log);
352      }
353    }
354  }
355}
356
357function createVariableInitStatement(node: ts.PropertyDeclaration, decorator: string,
358  log: LogInfo[], program: ts.Program, context: ts.TransformationContext, hasPreview: boolean,
359  interfaceNode: ts.InterfaceDeclaration): ts.Statement {
360  const name: ts.Identifier = node.name as ts.Identifier;
361  let type: ts.TypeNode;
362  let updateState: ts.ExpressionStatement;
363  if (node.type) {
364    type = node.type;
365  }
366  switch (decorator) {
367    case COMPONENT_NON_DECORATOR:
368      updateState = updateNormalProperty(node, name, log, context);
369      break;
370    case COMPONENT_STATE_DECORATOR:
371    case COMPONENT_PROVIDE_DECORATOR:
372      updateState = updateObservedProperty(node, name, type, program);
373      break;
374    case COMPONENT_LINK_DECORATOR:
375      wrongDecoratorInPreview(node, COMPONENT_LINK_DECORATOR, hasPreview, log);
376      updateState = updateSynchedPropertyTwoWay(name, type, program);
377      break;
378    case COMPONENT_PROP_DECORATOR:
379      wrongDecoratorInPreview(node, COMPONENT_PROP_DECORATOR, hasPreview, log);
380      updateState = updateSynchedPropertyOneWay(name, type, decorator, log, program);
381      break;
382    case COMPONENT_STORAGE_PROP_DECORATOR:
383    case COMPONENT_STORAGE_LINK_DECORATOR:
384      const setFuncName: string = decorator === COMPONENT_STORAGE_PROP_DECORATOR ?
385        APP_STORAGE_SET_AND_PROP : APP_STORAGE_SET_AND_LINK;
386      updateState = updateStoragePropAndLinkProperty(node, name, setFuncName, log);
387      break;
388    case COMPONENT_OBJECT_LINK_DECORATOR:
389      updateState = updateSynchedPropertyNesedObject(name, type, decorator, log);
390      break;
391    case COMPONENT_CONSUME_DECORATOR:
392      wrongDecoratorInPreview(node, COMPONENT_CONSUME_DECORATOR, hasPreview, log);
393      updateState = updateConsumeProperty(node, name);
394      break;
395  }
396  const members = interfaceNode.members;
397  members.push(ts.factory.createPropertySignature(undefined, name,
398    ts.factory.createToken(ts.SyntaxKind.QuestionToken), type));
399  interfaceNode = ts.factory.updateInterfaceDeclaration(interfaceNode, undefined,
400    interfaceNode.modifiers, interfaceNode.name, interfaceNode.typeParameters,
401    interfaceNode.heritageClauses, members);
402  return updateState;
403}
404
405function wrongDecoratorInPreview(node: ts.PropertyDeclaration, decorator: string,
406  hasPreview: boolean, log: LogInfo[]) {
407  if (hasPreview) {
408    log.push({
409      type: LogType.WARN,
410      message: `The variable with ${decorator} in component with @Preview may ` +
411        `cause error in component preview mode`,
412      pos: node.getStart()
413    });
414  }
415}
416
417function createUpdateParams(name: ts.Identifier, decorator: string): ts.Statement {
418  let updateParamsNode: ts.Statement;
419  switch (decorator) {
420    case COMPONENT_NON_DECORATOR:
421    case COMPONENT_STATE_DECORATOR:
422    case COMPONENT_PROVIDE_DECORATOR:
423    case COMPONENT_CUSTOM_DECORATOR:
424      updateParamsNode = createUpdateParamsWithIf(name);
425      break;
426    case COMPONENT_PROP_DECORATOR:
427      updateParamsNode = createUpdateParamsWithoutIf(name);
428      break;
429    case COMPONENT_BUILDERPARAM_DECORATOR:
430      updateParamsNode = createUpdateParamsWithoutIf(name);
431      break;
432    case COMPONENT_OBJECT_LINK_DECORATOR:
433      updateParamsNode = createUpdateParamsWithSet(name);
434      break;
435  }
436  return updateParamsNode;
437}
438
439function createUpdateParamsWithIf(name: ts.Identifier): ts.IfStatement {
440  return ts.factory.createIfStatement(ts.factory.createBinaryExpression(
441    ts.factory.createPropertyAccessExpression(
442      ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS),
443      ts.factory.createIdentifier(name.escapedText.toString())),
444    ts.factory.createToken(ts.SyntaxKind.ExclamationEqualsEqualsToken),
445    ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)), ts.factory.createBlock([
446    createUpdateParamsWithoutIf(name)], true), undefined);
447}
448
449function createUpdateParamsWithoutIf(name: ts.Identifier): ts.ExpressionStatement {
450  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
451    createPropertyAccessExpressionWithThis(name.getText()),
452    ts.factory.createToken(ts.SyntaxKind.EqualsToken),
453    createPropertyAccessExpressionWithParams(name.getText())));
454}
455
456function createUpdateParamsWithSet(name: ts.Identifier): ts.ExpressionStatement {
457  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
458    ts.factory.createPropertyAccessExpression(createPropertyAccessExpressionWithThis(`__${name.getText()}`),
459      ts.factory.createIdentifier(CREATE_SET_METHOD)), undefined,
460    [createPropertyAccessExpressionWithParams(name.getText())]));
461}
462
463function updateNormalProperty(node: ts.PropertyDeclaration, name: ts.Identifier,
464  log: LogInfo[], context: ts.TransformationContext): ts.ExpressionStatement {
465  const init: ts.Expression =
466    ts.visitNode(node.initializer, visitDialogController);
467  function visitDialogController(node: ts.Node): ts.Node {
468    if (isProperty(node)) {
469      node = createReference(node as ts.PropertyAssignment);
470    }
471    return ts.visitEachChild(node, visitDialogController, context);
472  }
473  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
474    createPropertyAccessExpressionWithThis(name.getText()),
475    ts.factory.createToken(ts.SyntaxKind.EqualsToken), init ||
476    ts.factory.createIdentifier(COMPONENT_CONSTRUCTOR_UNDEFINED)));
477}
478
479function updateObservedProperty(item: ts.PropertyDeclaration, name: ts.Identifier,
480  type: ts.TypeNode, program: ts.Program): ts.ExpressionStatement {
481  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
482    createPropertyAccessExpressionWithThis(`__${name.getText()}`),
483    ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression(
484      ts.factory.createIdentifier(isSimpleType(type, program) ? OBSERVED_PROPERTY_SIMPLE :
485        OBSERVED_PROPERTY_OBJECT), undefined, [item.initializer, ts.factory.createThis(),
486        ts.factory.createStringLiteral(name.escapedText.toString())])));
487}
488
489function updateSynchedPropertyTwoWay(nameIdentifier: ts.Identifier, type: ts.TypeNode,
490  program: ts.Program): ts.ExpressionStatement {
491  const name: string = nameIdentifier.escapedText.toString();
492  const functionName: string = isSimpleType(type, program) ?
493    SYNCHED_PROPERTY_SIMPLE_TWO_WAY : SYNCHED_PROPERTY_OBJECT_TWO_WAY;
494  return createInitExpressionStatementForDecorator(name, functionName,
495    createPropertyAccessExpressionWithParams(name));
496}
497
498function updateSynchedPropertyOneWay(nameIdentifier: ts.Identifier, type: ts.TypeNode,
499  decoractor: string, log: LogInfo[], program: ts.Program): ts.ExpressionStatement {
500  const name: string = nameIdentifier.escapedText.toString();
501  if (isSimpleType(type, program)) {
502    return createInitExpressionStatementForDecorator(name, SYNCHED_PROPERTY_SIMPLE_ONE_WAY,
503      createPropertyAccessExpressionWithParams(name));
504  } else {
505    validateNonSimpleType(nameIdentifier, decoractor, log);
506  }
507}
508
509function updateStoragePropAndLinkProperty(node: ts.PropertyDeclaration, name: ts.Identifier,
510  setFuncName: string, log: LogInfo[]): ts.ExpressionStatement {
511  if (isSingleKey(node)) {
512    const key: string = getDecoratorKey(node);
513    return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
514      createPropertyAccessExpressionWithThis(`__${name.getText()}`),
515      ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression(
516        ts.factory.createPropertyAccessExpression(ts.factory.createCallExpression(
517          ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(APP_STORAGE),
518            ts.factory.createIdentifier(APP_STORAGE_GET_OR_SET)), undefined, []),
519        ts.factory.createIdentifier(setFuncName)), undefined, [ts.factory.createStringLiteral(key),
520          node.initializer, ts.factory.createThis()])));
521  } else {
522    validateAppStorageDecoractorsNonSingleKey(node, log);
523  }
524}
525
526function getDecoratorKey(node: ts.PropertyDeclaration): string {
527  let key: string;
528  // @ts-ignore
529  const keyNameNode: ts.Node = node.decorators[0].expression.arguments[0];
530  if (ts.isIdentifier(keyNameNode)) {
531    key = keyNameNode.getText();
532    decoratorParamSet.add(key);
533  } else if (ts.isStringLiteral(keyNameNode)) {
534    key = keyNameNode.text;
535  }
536  return key;
537}
538
539function updateSynchedPropertyNesedObject(nameIdentifier: ts.Identifier,
540  type: ts.TypeNode, decoractor: string, log: LogInfo[]): ts.ExpressionStatement {
541  if (isObservedClassType(type)) {
542    return createInitExpressionStatementForDecorator(nameIdentifier.getText(), SYNCHED_PROPERTY_NESED_OBJECT,
543      createPropertyAccessExpressionWithParams(nameIdentifier.getText()));
544  } else {
545    validateNonObservedClassType(nameIdentifier, decoractor, log);
546  }
547}
548
549function updateConsumeProperty(node: ts.PropertyDeclaration,
550  nameIdentifier: ts.Identifier): ts.ExpressionStatement {
551  const name: string = nameIdentifier.getText();
552  let propertyOrAliasName: string;
553  if (isSingleKey(node)) {
554    propertyOrAliasName = getDecoratorKey(node);
555  } else {
556    propertyOrAliasName = name;
557  }
558  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
559    createPropertyAccessExpressionWithThis(`__${name}`),
560    ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createCallExpression(
561      createPropertyAccessExpressionWithThis(INITIALIZE_CONSUME_FUNCTION), undefined, [
562        ts.factory.createStringLiteral(propertyOrAliasName), ts.factory.createStringLiteral(name)])));
563}
564
565function createCustomComponentBuilderArrowFunction(parent: ts.PropertyDeclaration,
566  jsDialog: ts.Identifier, newExp: ts.Expression): ts.ArrowFunction {
567  return ts.factory.createArrowFunction(undefined, undefined, [], undefined,
568    ts.factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), ts.factory.createBlock([
569      ts.factory.createVariableStatement(undefined, ts.factory.createVariableDeclarationList(
570        [ts.factory.createVariableDeclaration(jsDialog, undefined, undefined, newExp)],
571        ts.NodeFlags.Let)), ts.factory.createExpressionStatement(ts.factory.createCallExpression(
572        ts.factory.createPropertyAccessExpression(jsDialog,
573          ts.factory.createIdentifier(SET_CONTROLLER_METHOD)), undefined,
574        [ts.factory.createPropertyAccessExpression(ts.factory.createThis(),
575            parent.name as ts.Identifier)])), ts.factory.createExpressionStatement(
576        createViewCreate(jsDialog))], true));
577}
578
579export function createViewCreate(node: ts.NewExpression | ts.Identifier): ts.CallExpression {
580  return createFunction(ts.factory.createIdentifier(BASE_COMPONENT_NAME),
581    ts.factory.createIdentifier(COMPONENT_CREATE_FUNCTION), ts.factory.createNodeArray([node]));
582}
583
584export function createCustomComponentNewExpression(node: ts.CallExpression): ts.NewExpression {
585  const newNode: ts.NewExpression = ts.factory.createNewExpression(node.expression,
586    node.typeArguments, node.arguments.length ? node.arguments : []);
587  return addCustomComponentId(newNode);
588}
589
590function addCustomComponentId(node: ts.NewExpression): ts.NewExpression {
591  for (const item of componentCollection.customComponents) {
592    componentInfo.componentNames.add(item);
593  }
594  componentInfo.componentNames.forEach((name: string) => {
595    const nodeIdentifier: ts.Identifier | ts.PropertyAccessExpression =
596      node.expression as ts.Identifier | ts.PropertyAccessExpression;
597    let argumentsArray: ts.Expression[];
598    if (node.arguments && node.arguments.length) {
599      argumentsArray = Array.from(node.arguments);
600    }
601    if (nodeIdentifier && (ts.isIdentifier(nodeIdentifier) &&
602      nodeIdentifier.escapedText === name || ts.isPropertyAccessExpression(nodeIdentifier) &&
603      ts.isIdentifier(nodeIdentifier.name) && nodeIdentifier.name.escapedText === name)) {
604      if (!argumentsArray) {
605        argumentsArray = [ts.factory.createObjectLiteralExpression([], true)];
606      }
607      argumentsArray.unshift(ts.factory.createStringLiteral((++componentInfo.id).toString()),
608        ts.factory.createThis());
609      node =
610        ts.factory.updateNewExpression(node, node.expression, node.typeArguments, argumentsArray);
611    } else if (argumentsArray) {
612      node =
613        ts.factory.updateNewExpression(node, node.expression, node.typeArguments, argumentsArray);
614    }
615  });
616  return node;
617}
618
619function createInitExpressionStatementForDecorator(propertyName: string, functionName: string,
620  parameterNode: ts.Expression): ts.ExpressionStatement {
621  return ts.factory.createExpressionStatement(ts.factory.createBinaryExpression(
622    createPropertyAccessExpressionWithThis(`__${propertyName}`),
623    ts.factory.createToken(ts.SyntaxKind.EqualsToken), ts.factory.createNewExpression(
624      ts.factory.createIdentifier(functionName), undefined, [parameterNode, ts.factory.createThis(),
625        ts.factory.createStringLiteral(propertyName)])));
626}
627
628function createPropertyAccessExpressionWithParams(propertyName: string): ts.PropertyAccessExpression {
629  return ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier(CREATE_CONSTRUCTOR_PARAMS),
630    ts.factory.createIdentifier(propertyName));
631}
632
633function createPropertyAccessExpressionWithThis(propertyName: string): ts.PropertyAccessExpression {
634  return ts.factory.createPropertyAccessExpression(ts.factory.createThis(),
635    ts.factory.createIdentifier(propertyName));
636}
637
638function addAddProvidedVar(node: ts.PropertyDeclaration, name: ts.Identifier,
639  decoratorName: string, updateState: ts.Statement[]): void {
640  if (decoratorName === COMPONENT_PROVIDE_DECORATOR) {
641    let parameterName: string;
642    if (isSingleKey(node)) {
643      parameterName = getDecoratorKey(node);
644      updateState.push(createAddProvidedVar(parameterName, name));
645    }
646    if (parameterName !== name.getText()) {
647      updateState.push(createAddProvidedVar(name.getText(), name));
648    }
649  }
650}
651
652function createAddProvidedVar(propertyOrAliasName: string,
653  name: ts.Identifier): ts.ExpressionStatement {
654  return ts.factory.createExpressionStatement(ts.factory.createCallExpression(
655    createPropertyAccessExpressionWithThis(ADD_PROVIDED_VAR), undefined, [
656      ts.factory.createStringLiteral(propertyOrAliasName),
657      createPropertyAccessExpressionWithThis(`__${name.getText()}`)]));
658}
659
660function createGetAccessor(item: ts.Identifier, express: string): ts.GetAccessorDeclaration {
661  const getAccessorStatement: ts.GetAccessorDeclaration =
662    ts.factory.createGetAccessorDeclaration(undefined, undefined, item, [], undefined,
663      ts.factory.createBlock([ts.factory.createReturnStatement(
664        ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
665          createPropertyAccessExpressionWithThis(`__${item.getText()}`),
666          ts.factory.createIdentifier(express)), undefined, []))], true));
667  return getAccessorStatement;
668}
669
670function createSetAccessor(item: ts.Identifier, express: string, type: ts.TypeNode):
671  ts.SetAccessorDeclaration {
672  const setAccessorStatement: ts.SetAccessorDeclaration =
673    ts.factory.createSetAccessorDeclaration(undefined, undefined, item,
674      [ts.factory.createParameterDeclaration(undefined, undefined, undefined,
675        ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER), undefined, type,
676        undefined)], ts.factory.createBlock([ts.factory.createExpressionStatement(
677        ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(
678          createPropertyAccessExpressionWithThis(`__${item.getText()}`),
679          ts.factory.createIdentifier(express)), undefined,
680        [ts.factory.createIdentifier(CREATE_NEWVALUE_IDENTIFIER)]))], true));
681  return setAccessorStatement;
682}
683
684function isForbiddenUseStateType(typeNode: ts.TypeNode): boolean {
685  if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName) &&
686    forbiddenUseStateType.has(typeNode.typeName.getText())) {
687    return true;
688  }
689  return false;
690}
691
692export function isSimpleType(typeNode: ts.TypeNode, program: ts.Program, log?: LogInfo[]): boolean {
693  typeNode = typeNode || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
694  let checker: ts.TypeChecker;
695  if (globalProgram.program) {
696    checker = globalProgram.program.getTypeChecker();
697  } else if (globalProgram.watchProgram) {
698    checker = globalProgram.watchProgram.getCurrentProgram().getProgram().getTypeChecker();
699  } else if (program) {
700    checker = program.getTypeChecker();
701  }
702  const enumType: ts.SyntaxKind = getEnumType(typeNode, checker);
703  if (simpleTypes.has(enumType || typeNode.kind) || isEnumtype(typeNode)) {
704    return true;
705  } else if (ts.isUnionTypeNode(typeNode) && typeNode.types) {
706    const types: ts.NodeArray<ts.TypeNode> = typeNode.types;
707    let basicType: boolean = false;
708    let referenceType: boolean = false;
709    for (let i = 0; i < types.length; i++) {
710      const enumType: ts.SyntaxKind = getEnumType(types[i], checker);
711      if (simpleTypes.has(enumType || types[i].kind) || isEnumtype(typeNode)) {
712        basicType = true;
713      } else {
714        referenceType = true;
715      }
716      if (basicType && referenceType && log) {
717        validateVariableType(typeNode, log);
718        return false;
719      }
720    }
721    return true;
722  }
723  return false;
724}
725
726function getEnumType(typeNode: ts.TypeNode, checker: ts.TypeChecker): ts.SyntaxKind {
727  if (!checker) {
728    return;
729  }
730  typeNode = typeNode || ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword);
731  if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
732    const type: ts.Type =
733      checker.getBaseTypeOfLiteralType(checker.getTypeAtLocation(typeNode.typeName));
734    if (type.symbol && type.symbol.valueDeclaration) {
735      return type.symbol.valueDeclaration.kind;
736    }
737  }
738}
739
740function isEnumtype(typeNode: ts.TypeNode): boolean {
741  if (ts.isTypeReferenceNode(typeNode) && ts.isIdentifier(typeNode.typeName)) {
742    return enumCollection.has(typeNode.typeName.getText());
743  }
744}
745
746function isObservedClassType(type: ts.TypeNode): boolean {
747  if (ts.isTypeReferenceNode(type) && observedClassCollection.has(type.getText())) {
748    return true;
749  } else if (ts.isUnionTypeNode(type) && type.types) {
750    const types: ts.NodeArray<ts.TypeNode> = type.types;
751    for (let i = 0; i < types.length; i++) {
752      if (!observedClassCollection.has(types[i].getText())) {
753        return false;
754      }
755    }
756    return true;
757  }
758  return false;
759}
760
761function validateAppStorageDecoractorsNonSingleKey(node: ts.PropertyDeclaration,
762  log: LogInfo[]): void {
763  if (ts.isIdentifier(node.decorators[0].expression)) {
764    validateDecoratorNonSingleKey(node.decorators[0].expression, log);
765  } else if (ts.isCallExpression(node.decorators[0].expression) &&
766    ts.isIdentifier(node.decorators[0].expression.expression)) {
767    validateDecoratorNonSingleKey(node.decorators[0].expression.expression, log);
768  }
769}
770
771function isSingleKey(node: ts.PropertyDeclaration): boolean {
772  if (ts.isCallExpression(node.decorators[0].expression) &&
773  node.decorators[0].expression.arguments &&
774  node.decorators[0].expression.arguments.length === 1 &&
775  (ts.isIdentifier(node.decorators[0].expression.arguments[0]) ||
776  ts.isStringLiteral(node.decorators[0].expression.arguments[0]))) {
777    return true;
778  }
779}
780
781function validateMultiDecorators(name: ts.Identifier, log: LogInfo[]): void {
782  log.push({
783    type: LogType.ERROR,
784    message: `The property '${name.escapedText.toString()}' cannot have mutilate state management decorators.`,
785    pos: name.getStart()
786  });
787}
788
789function validateDecoratorNonSingleKey(decoratorsIdentifier: ts.Identifier,
790  log: LogInfo[]): void {
791  log.push({
792    type: LogType.ERROR,
793    message: `The decorator ${decoratorsIdentifier.escapedText.toString()} should have a single key.`,
794    pos: decoratorsIdentifier.getStart()
795  });
796}
797
798function validatePropertyNonDefaultValue(propertyName: ts.Identifier, decorator: string,
799  log: LogInfo[]): void {
800  log.push({
801    type: LogType.ERROR,
802    message: `The ${decorator} property '${propertyName.getText()}' must be specified a default value.`,
803    pos: propertyName.getStart()
804  });
805}
806
807function validatePropertyDefaultValue(propertyName: ts.Identifier, decorator: string,
808  log: LogInfo[]): void {
809  log.push({
810    type: LogType.ERROR,
811    message: `The ${decorator} property '${propertyName.getText()}' cannot be specified a default value.`,
812    pos: propertyName.getStart()
813  });
814}
815
816function validatePropertyNonType(propertyName: ts.Identifier, log: LogInfo[]): void {
817  log.push({
818    type: LogType.ERROR,
819    message: `The property '${propertyName.getText()}' must specify a type.`,
820    pos: propertyName.getStart()
821  });
822}
823
824function validateNonSimpleType(propertyName: ts.Identifier, decorator: string,
825  log: LogInfo[]): void {
826  log.push({
827    type: LogType.ERROR,
828    message: `The type of the ${decorator} property '${propertyName.getText()}' ` +
829      `can only be string, number or boolean.`,
830    pos: propertyName.getStart()
831  });
832}
833
834function validateNonObservedClassType(propertyName: ts.Identifier, decorator: string,
835  log: LogInfo[]): void {
836  log.push({
837    type: LogType.ERROR,
838    message: `The type of the ${decorator} property '${propertyName.getText()}' can only be ` +
839      `objects of classes decorated with ${COMPONENT_OBSERVED_DECORATOR} class decorator in ets (not ts).`,
840    pos: propertyName.getStart()
841  });
842}
843
844function validateHasIllegalQuestionToken(propertyName: ts.Identifier, decorator: string,
845  log: LogInfo[]): void {
846  log.push({
847    type: LogType.WARN,
848    message: `The ${decorator} property '${propertyName.getText()}' cannot be an optional parameter.`,
849    pos: propertyName.getStart()
850  });
851}
852
853function validateHasIllegalDecoratorInEntry(parentName: ts.Identifier, propertyName: ts.Identifier,
854  decorator: string, log: LogInfo[]): void {
855  log.push({
856    type: LogType.WARN,
857    message: `The @Entry component '${parentName.getText()}' cannot have the ` +
858      `${decorator} property '${propertyName.getText()}'.`,
859    pos: propertyName.getStart()
860  });
861}
862
863function validateForbiddenUseStateType(propertyName: ts.Identifier, decorator: string, type: string,
864  log: LogInfo[]): void {
865  log.push({
866    type: LogType.ERROR,
867    message: `The ${decorator} property '${propertyName.getText()}' cannot be a '${type}' object.`,
868    pos: propertyName.getStart()
869  });
870}
871
872function validateDuplicateDecorator(decorator: ts.Decorator, log: LogInfo[]): void {
873  log.push({
874    type: LogType.ERROR,
875    message: `The decorator '${decorator.getText()}' cannot have the same name as the built-in ` +
876      `style attribute '${decorator.getText().replace('@', '')}'.`,
877    pos: decorator.getStart()
878  });
879}
880
881function validateWatchDecorator(propertyName: ts.Identifier, length: number, log: LogInfo[]): boolean {
882  if (length === 1) {
883    log.push({
884      type: LogType.ERROR,
885      message: `Regular variable '${propertyName.escapedText.toString()}' can not be decorated with @Watch.`,
886      pos: propertyName.getStart()
887    });
888    return false;
889  }
890  return true;
891}
892
893function validateWatchParam(type: LogType, pos: number, log: LogInfo[]): void {
894  log.push({
895    type: type,
896    message: 'The parameter should be a string.',
897    pos: pos
898  });
899}
900
901function validateVariableType(typeNode: ts.TypeNode, log: LogInfo[]): void {
902  log.push({
903    type: LogType.ERROR,
904    message: 'The state variable type of a struct component cannot be declared by both a simple type and an object type.',
905    pos: typeNode.getStart()
906  });
907}
908
909function validateCustomDecorator(decorators: ts.NodeArray<ts.Decorator>, log: LogInfo[]): boolean {
910  let hasInnerDecorator: boolean = false;
911  let hasCustomDecorator: boolean = false;
912  let innerDecorator: ts.Decorator;
913  for(let i = 0; i < decorators.length; i++) {
914    let decorator: ts.Decorator = decorators[i];
915    const decoratorName: string = decorator.getText().replace(/\(.*\)$/, '').trim();
916    if (INNER_COMPONENT_MEMBER_DECORATORS.has(decoratorName)) {
917      hasInnerDecorator = true;
918      innerDecorator = innerDecorator ? innerDecorator : decorator;
919    } else {
920      hasCustomDecorator = true;
921    }
922  }
923  if (hasCustomDecorator && hasInnerDecorator) {
924    log.push({
925      type: LogType.ERROR,
926      message: `The inner decorator ${innerDecorator.getText()} cannot be used together with custom decorator.`,
927      pos: innerDecorator.getStart()
928    });
929  } else if(!hasInnerDecorator) {
930    return true;
931  }
932  return false;
933}
934