|  | // Copyright (C) 2019 The Android Open Source Project | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | // http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | import {LegacyLifecycleMethodsArray, LegacyPolymerComponent} from './polymerComponentParser'; | 
|  | import {LifecycleMethodsBuilder} from './lifecycleMethodsBuilder'; | 
|  | import {ClassBasedPolymerElement, PolymerElementBuilder} from './polymerElementBuilder'; | 
|  | import * as codeUtils from '../utils/codeUtils'; | 
|  | import * as ts from 'typescript'; | 
|  |  | 
|  | export class PolymerFuncToClassBasedConverter { | 
|  | public static convert(component: LegacyPolymerComponent): ClassBasedPolymerElement { | 
|  | const legacySettings = component.componentSettings; | 
|  | const reservedDeclarations = legacySettings.reservedDeclarations; | 
|  |  | 
|  | if(!reservedDeclarations.is) { | 
|  | throw new Error("Legacy component doesn't have 'is' property"); | 
|  | } | 
|  | const className = this.generateClassNameFromTagName(reservedDeclarations.is.data); | 
|  | const updater = new PolymerElementBuilder(component, className); | 
|  | updater.addIsAccessor(reservedDeclarations.is.data); | 
|  |  | 
|  | if(reservedDeclarations.properties) { | 
|  | updater.addPolymerPropertiesAccessor(reservedDeclarations.properties); | 
|  | } | 
|  |  | 
|  | updater.addMixin("Polymer.Element"); | 
|  | updater.addMixin("Polymer.LegacyElementMixin"); | 
|  | updater.addMixin("Polymer.GestureEventListeners"); | 
|  |  | 
|  | if(reservedDeclarations._legacyUndefinedCheck) { | 
|  | updater.addMixin("Polymer.LegacyDataMixin"); | 
|  | } | 
|  |  | 
|  | if(reservedDeclarations.behaviors) { | 
|  | updater.addMixin("Polymer.mixinBehaviors", [reservedDeclarations.behaviors.data]); | 
|  | const mixinNames = this.getMixinNamesFromBehaviors(reservedDeclarations.behaviors.data); | 
|  | const jsDocLines = mixinNames.map(mixinName => { | 
|  | return `@appliesMixin ${mixinName}`; | 
|  | }); | 
|  | updater.addClassJSDocComments(jsDocLines); | 
|  | } | 
|  |  | 
|  | if(reservedDeclarations.observers) { | 
|  | updater.addPolymerPropertiesObservers(reservedDeclarations.observers.data); | 
|  | } | 
|  |  | 
|  | if(reservedDeclarations.keyBindings) { | 
|  | updater.addKeyBindings(reservedDeclarations.keyBindings.data); | 
|  | } | 
|  |  | 
|  |  | 
|  | const lifecycleBuilder = new LifecycleMethodsBuilder(); | 
|  | if (reservedDeclarations.listeners) { | 
|  | lifecycleBuilder.addListeners(reservedDeclarations.listeners.data, legacySettings.ordinaryMethods); | 
|  | } | 
|  |  | 
|  | if (reservedDeclarations.hostAttributes) { | 
|  | lifecycleBuilder.addHostAttributes(reservedDeclarations.hostAttributes.data); | 
|  | } | 
|  |  | 
|  | for(const name of LegacyLifecycleMethodsArray) { | 
|  | const existingMethod = legacySettings.lifecycleMethods.get(name); | 
|  | if(existingMethod) { | 
|  | lifecycleBuilder.addLegacyLifecycleMethod(name, existingMethod) | 
|  | } | 
|  | } | 
|  |  | 
|  | const newLifecycleMethods = lifecycleBuilder.buildNewMethods(); | 
|  | updater.addLifecycleMethods(newLifecycleMethods); | 
|  |  | 
|  |  | 
|  | updater.addOrdinaryMethods(legacySettings.ordinaryMethods); | 
|  | updater.addOrdinaryGetAccessors(legacySettings.ordinaryGetAccessors); | 
|  | updater.addOrdinaryShorthandProperties(legacySettings.ordinaryShorthandProperties); | 
|  | updater.addOrdinaryPropertyAssignments(legacySettings.ordinaryPropertyAssignments); | 
|  |  | 
|  | return updater.build(); | 
|  | } | 
|  |  | 
|  | private static generateClassNameFromTagName(tagName: string) { | 
|  | let result = ""; | 
|  | let nextUppercase = true; | 
|  | for(const ch of tagName) { | 
|  | if (ch === '-') { | 
|  | nextUppercase = true; | 
|  | continue; | 
|  | } | 
|  | result += nextUppercase ? ch.toUpperCase() : ch; | 
|  | nextUppercase = false; | 
|  | } | 
|  | return result; | 
|  | } | 
|  |  | 
|  | private static getMixinNamesFromBehaviors(behaviors: ts.ArrayLiteralExpression): string[] { | 
|  | return behaviors.elements.map((expression) => { | 
|  | const propertyAccessExpression = codeUtils.assertNodeKind(expression, ts.SyntaxKind.PropertyAccessExpression) as ts.PropertyAccessExpression; | 
|  | const namespaceName = codeUtils.assertNodeKind(propertyAccessExpression.expression, ts.SyntaxKind.Identifier) as ts.Identifier; | 
|  | const behaviorName = propertyAccessExpression.name; | 
|  | if(namespaceName.text === 'Gerrit') { | 
|  | let behaviorNameText = behaviorName.text; | 
|  | const suffix = 'Behavior'; | 
|  | if(behaviorNameText.endsWith(suffix)) { | 
|  | behaviorNameText = | 
|  | behaviorNameText.substr(0, behaviorNameText.length - suffix.length); | 
|  | } | 
|  | const mixinName = behaviorNameText + 'Mixin'; | 
|  | return `${namespaceName.text}.${mixinName}` | 
|  | } else if(namespaceName.text === 'Polymer') { | 
|  | let behaviorNameText = behaviorName.text; | 
|  | if(behaviorNameText === "IronFitBehavior") { | 
|  | return "Polymer.IronFitMixin"; | 
|  | } else if(behaviorNameText === "IronOverlayBehavior") { | 
|  | return ""; | 
|  | } | 
|  | throw new Error(`Unsupported behavior: ${propertyAccessExpression.getText()}`); | 
|  | } | 
|  | throw new Error(`Unsupported behavior name ${expression.getFullText()}`) | 
|  | }).filter(name => name.length > 0); | 
|  | } | 
|  | } |