| /* |
| * |
| * Copyright (c) 2021 Project CHIP Authors |
| * |
| * 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 helpers from zap core |
| const zapPath = '../../../../../third_party/zap/repo/dist/src-electron/'; |
| const string = require(zapPath + 'util/string.js') |
| const templateUtil = require(zapPath + 'generator/template-util.js') |
| const zclHelper = require(zapPath + 'generator/helper-zcl.js') |
| |
| const ChipTypesHelper = require('../../../../../src/app/zap-templates/common/ChipTypesHelper.js'); |
| const TestHelper = require('../../../../../src/app/zap-templates/common/ClusterTestGeneration.js'); |
| const StringHelper = require('../../../../../src/app/zap-templates/common/StringHelper.js'); |
| const appHelper = require('../../../../../src/app/zap-templates/templates/app/helper.js'); |
| |
| function asObjectiveCBasicType(type, options) |
| { |
| if (StringHelper.isOctetString(type)) { |
| return options.hash.is_mutable ? 'NSMutableData *' : 'NSData *'; |
| } else if (StringHelper.isCharString(type)) { |
| return options.hash.is_mutable ? 'NSMutableString *' : 'NSString *'; |
| } else { |
| return ChipTypesHelper.asBasicType(this.chipType); |
| } |
| } |
| |
| /** |
| * Converts an expression involving possible variables whose types are objective C objects into an expression whose type is a C++ |
| * type |
| */ |
| async function asTypedExpressionFromObjectiveC(value, type) |
| { |
| const valueIsANumber = !isNaN(value); |
| if (!value || valueIsANumber) { |
| return appHelper.asTypedLiteral.call(this, value, type); |
| } |
| |
| const tokens = value.split(' '); |
| if (tokens.length < 2) { |
| return appHelper.asTypedLiteral.call(this, value, type); |
| } |
| |
| let expr = []; |
| for (let i = 0; i < tokens.length; i++) { |
| const token = tokens[i]; |
| if ([ '+', '-', '/', '*', '%' ].includes(token)) { |
| expr[i] = token; |
| } else if (!isNaN(token.replace(/ULL$|UL$|U$|LL$|L$/i, ''))) { |
| expr[i] = await appHelper.asTypedLiteral.call(this, token, type); |
| } else { |
| const variableType = TestHelper.chip_tests_variables_get_type.call(this, token); |
| const asType = await asObjectiveCNumberType.call(this, token, variableType, true); |
| expr[i] = `[${token} ${asType}Value]`; |
| } |
| } |
| |
| return expr.join(' '); |
| } |
| |
| function asObjectiveCNumberType(label, type, asLowerCased) |
| { |
| function fn(pkgId) |
| { |
| const options = { 'hash' : {} }; |
| return zclHelper.asUnderlyingZclType.call(this, type, options) |
| .then(zclType => { |
| const basicType = ChipTypesHelper.asBasicType(zclType); |
| switch (basicType) { |
| case 'bool': |
| return 'Bool'; |
| case 'uint8_t': |
| return 'UnsignedChar'; |
| case 'uint16_t': |
| return 'UnsignedShort'; |
| case 'uint32_t': |
| return 'UnsignedInt'; |
| case 'uint64_t': |
| return 'UnsignedLongLong'; |
| case 'int8_t': |
| return 'Char'; |
| case 'int16_t': |
| return 'Short'; |
| case 'int32_t': |
| return 'Int'; |
| case 'int64_t': |
| return 'LongLong'; |
| case 'float': |
| return 'Float'; |
| case 'double': |
| return 'Double'; |
| default: |
| error = label + ': Unhandled underlying type ' + zclType + ' for original type ' + type; |
| throw error; |
| } |
| }) |
| .then(typeName => asLowerCased ? (typeName[0].toLowerCase() + typeName.substring(1)) : typeName); |
| } |
| |
| const promise = templateUtil.ensureZclPackageId(this).then(fn.bind(this)).catch(err => console.log(err)); |
| return templateUtil.templatePromise(this.global, promise) |
| } |
| |
| async function asObjectiveCClass(type, cluster, options) |
| { |
| let pkgId = await templateUtil.ensureZclPackageId(this); |
| let isStruct = await zclHelper.isStruct(this.global.db, type, pkgId).then(zclType => zclType != 'unknown'); |
| |
| if ((this.isArray || this.entryType || options.hash.forceList) && !options.hash.forceNotList) { |
| return 'NSArray'; |
| } |
| |
| if (StringHelper.isOctetString(type)) { |
| return 'NSData'; |
| } |
| |
| if (StringHelper.isCharString(type)) { |
| return 'NSString'; |
| } |
| |
| if (isStruct) { |
| return `MTR${appHelper.asUpperCamelCase(cluster)}Cluster${appHelper.asUpperCamelCase(type)}`; |
| } |
| |
| return 'NSNumber'; |
| } |
| |
| async function asObjectiveCType(type, cluster, options) |
| { |
| let typeStr = await asObjectiveCClass.call(this, type, cluster, options); |
| if (this.isNullable || this.isOptional) { |
| typeStr = `${typeStr} * _Nullable`; |
| } else { |
| typeStr = `${typeStr} * _Nonnull`; |
| } |
| |
| return typeStr; |
| } |
| |
| function asStructPropertyName(prop) |
| { |
| prop = appHelper.asLowerCamelCase(prop); |
| |
| // If prop is now "description", we need to rename it, because that's |
| // reserved. |
| if (prop == "description") { |
| return "descriptionString"; |
| } |
| |
| // If prop starts with a sequence of capital letters (which can happen for |
| // output of asLowerCamelCase if the original string started that way, |
| // lowercase all but the last one. |
| return prop.replace(/^([A-Z]+)([A-Z])/, (match, p1, p2) => { return p1.toLowerCase() + p2 }); |
| } |
| |
| function asGetterName(prop) |
| { |
| let propName = asStructPropertyName(prop); |
| if (propName.match(/^new[A-Z]/) || propName == "count") { |
| return "get" + appHelper.asUpperCamelCase(prop); |
| } |
| return propName; |
| } |
| |
| function commandHasRequiredField(command) |
| { |
| return command.arguments.some(arg => !arg.isOptional); |
| } |
| |
| /** |
| * Produce a reasonable name for an Objective C enum for the given cluster name |
| * and enum label. Because a lot of our enum labels already have the cluster |
| * name prefixed (e.g. NetworkCommissioning*, or the IdentifyIdentifyType that |
| * has it prefixed _twice_) just concatenating the two gives overly verbose |
| * names in a few cases (e.g. "IdentifyIdentifyIdentifyType"). |
| * |
| * This function strips out the redundant cluster names, and strips off trailing |
| * "Enum" bits on the enum names while we're here. |
| */ |
| function objCEnumName(clusterName, enumLabel) |
| { |
| clusterName = appHelper.asUpperCamelCase(clusterName); |
| enumLabel = appHelper.asUpperCamelCase(enumLabel); |
| // Some enum names have one or more copies of the cluster name at the |
| // beginning. |
| while (enumLabel.startsWith(clusterName)) { |
| enumLabel = enumLabel.substring(clusterName.length); |
| } |
| |
| if (enumLabel.endsWith("Enum")) { |
| // Strip that off; it'll clearly be an enum anyway. |
| enumLabel = enumLabel.substring(0, enumLabel.length - "Enum".length); |
| } |
| |
| return "MTR" + clusterName + enumLabel; |
| } |
| |
| function objCEnumItemLabel(itemLabel) |
| { |
| // Check for the case when we're: |
| // 1. A single word (that's the regexp at the beginning, which matches the |
| // word-splitting regexp in string.toCamelCase). |
| // 2. All upper-case. |
| // |
| // This will get converted to lowercase except the first letter by |
| // asUpperCamelCase, which is not really what we want. |
| if (!/ |_|-|\//.test(itemLabel) && itemLabel.toUpperCase() == itemLabel) { |
| return itemLabel.replace(/[\.:]/g, ''); |
| } |
| |
| return appHelper.asUpperCamelCase(itemLabel); |
| } |
| |
| function hasArguments() |
| { |
| return !!this.arguments.length |
| } |
| |
| // |
| // Module exports |
| // |
| exports.asObjectiveCBasicType = asObjectiveCBasicType; |
| exports.asObjectiveCNumberType = asObjectiveCNumberType; |
| exports.asObjectiveCClass = asObjectiveCClass; |
| exports.asObjectiveCType = asObjectiveCType; |
| exports.asStructPropertyName = asStructPropertyName; |
| exports.asTypedExpressionFromObjectiveC = asTypedExpressionFromObjectiveC; |
| exports.asGetterName = asGetterName; |
| exports.commandHasRequiredField = commandHasRequiredField; |
| exports.objCEnumName = objCEnumName; |
| exports.objCEnumItemLabel = objCEnumItemLabel; |
| exports.hasArguments = hasArguments; |