001// -------------------------------------------------------------------------------- 002// Copyright 2002-2026 Echo Three, LLC 003// 004// Licensed under the Apache License, Version 2.0 (the "License"); 005// you may not use this file except in compliance with the License. 006// You may obtain a copy of the License at 007// 008// http://www.apache.org/licenses/LICENSE-2.0 009// 010// Unless required by applicable law or agreed to in writing, software 011// distributed under the License is distributed on an "AS IS" BASIS, 012// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013// See the License for the specific language governing permissions and 014// limitations under the License. 015// -------------------------------------------------------------------------------- 016 017package com.echothree.control.user.core.server.command; 018 019import com.echothree.control.user.core.common.edit.EntityAttributeEdit; 020import com.echothree.control.user.core.common.form.CreateEntityAttributeForm; 021import com.echothree.control.user.core.common.result.CoreResultFactory; 022import com.echothree.model.control.core.common.EntityAttributeTypes; 023import com.echothree.model.control.core.server.logic.EntityAttributeLogic; 024import com.echothree.model.control.core.server.logic.EntityTypeLogic; 025import com.echothree.model.control.party.common.PartyTypes; 026import com.echothree.model.control.security.common.SecurityRoleGroups; 027import com.echothree.model.control.security.common.SecurityRoles; 028import com.echothree.model.control.sequence.common.SequenceTypes; 029import com.echothree.model.control.sequence.server.control.SequenceControl; 030import com.echothree.model.control.sequence.server.logic.SequenceTypeLogic; 031import com.echothree.model.control.uom.server.logic.UnitOfMeasureTypeLogic; 032import com.echothree.model.control.workflow.server.logic.WorkflowLogic; 033import com.echothree.model.data.core.server.entity.EntityAttribute; 034import com.echothree.model.data.core.server.entity.EntityAttributeType; 035import com.echothree.model.data.sequence.server.entity.Sequence; 036import com.echothree.model.data.uom.server.entity.UnitOfMeasureType; 037import com.echothree.model.data.user.common.pk.UserVisitPK; 038import com.echothree.model.data.workflow.server.entity.Workflow; 039import com.echothree.util.common.command.BaseResult; 040import com.echothree.util.common.form.ValidationResult; 041import com.echothree.util.common.message.ExecutionErrors; 042import com.echothree.util.common.validation.FieldDefinition; 043import com.echothree.util.common.validation.FieldType; 044import com.echothree.util.server.control.BaseSimpleCommand; 045import com.echothree.util.server.control.CommandSecurityDefinition; 046import com.echothree.util.server.control.PartyTypeDefinition; 047import com.echothree.util.server.control.SecurityRoleDefinition; 048import com.echothree.util.server.validation.Validator; 049import java.util.List; 050import javax.enterprise.context.Dependent; 051import javax.inject.Inject; 052 053@Dependent 054public class CreateEntityAttributeCommand 055 extends BaseSimpleCommand<CreateEntityAttributeForm> { 056 057 private final static CommandSecurityDefinition COMMAND_SECURITY_DEFINITION; 058 private final static List<FieldDefinition> FORM_FIELD_DEFINITIONS; 059 060 private final static List<FieldDefinition> BLOB_FORM_FIELD_DEFINITIONS; 061 private final static List<FieldDefinition> STRING_FORM_FIELD_DEFINITIONS; 062 private final static List<FieldDefinition> INTEGER_FORM_FIELD_DEFINITIONS; 063 private final static List<FieldDefinition> LONG_FORM_FIELD_DEFINITIONS; 064 private final static List<FieldDefinition> LISTITEM_FORM_FIELD_DEFINITIONS; 065 private final static List<FieldDefinition> MULTIPLELISTITEM_FORM_FIELD_DEFINITIONS; 066 private final static List<FieldDefinition> WORKFLOW_FORM_FIELD_DEFINITIONS; 067 private final static List<FieldDefinition> OTHER_FORM_FIELD_DEFINITIONS; 068 069 static { 070 COMMAND_SECURITY_DEFINITION = new CommandSecurityDefinition(List.of( 071 new PartyTypeDefinition(PartyTypes.UTILITY.name(), null), 072 new PartyTypeDefinition(PartyTypes.EMPLOYEE.name(), List.of( 073 new SecurityRoleDefinition(SecurityRoleGroups.EntityAttribute.name(), SecurityRoles.Create.name()) 074 )) 075 )); 076 077 FORM_FIELD_DEFINITIONS = List.of( 078 new FieldDefinition("EntityRef", FieldType.ENTITY_REF, false, null, null), 079 new FieldDefinition("Uuid", FieldType.UUID, false, null, null), 080 new FieldDefinition("ComponentVendorName", FieldType.ENTITY_NAME, false, null, null), 081 new FieldDefinition("EntityTypeName", FieldType.ENTITY_TYPE_NAME, false, null, null), 082 new FieldDefinition("EntityAttributeName", FieldType.ENTITY_NAME, false, null, null), 083 new FieldDefinition("EntityAttributeTypeName", FieldType.ENTITY_NAME, true, null, null), 084 new FieldDefinition("TrackRevisions", FieldType.BOOLEAN, true, null, null), 085 new FieldDefinition("SortOrder", FieldType.SIGNED_INTEGER, true, null, null), 086 new FieldDefinition("Description", FieldType.STRING, false, 1L, 132L) 087 ); 088 089 BLOB_FORM_FIELD_DEFINITIONS = List.of( 090 new FieldDefinition("CheckContentWebAddress", FieldType.BOOLEAN, true, null, null), 091 new FieldDefinition("ValidationPattern", FieldType.NULL, false, null, null), 092 new FieldDefinition("UpperRangeIntegerValue", FieldType.NULL, false, null, null), 093 new FieldDefinition("UpperLimitIntegerValue", FieldType.NULL, false, null, null), 094 new FieldDefinition("LowerLimitIntegerValue", FieldType.NULL, false, null, null), 095 new FieldDefinition("LowerRangeIntegerValue", FieldType.NULL, false, null, null), 096 new FieldDefinition("UpperRangeLongValue", FieldType.NULL, false, null, null), 097 new FieldDefinition("UpperLimitLongValue", FieldType.NULL, false, null, null), 098 new FieldDefinition("LowerLimitLongValue", FieldType.NULL, false, null, null), 099 new FieldDefinition("LowerRangeLongValue", FieldType.NULL, false, null, null), 100 new FieldDefinition("UnitOfMeasureKindName", FieldType.NULL, false, null, null), 101 new FieldDefinition("UnitOfMeasureTypeName", FieldType.NULL, false, null, null), 102 new FieldDefinition("EntityListItemSequenceName", FieldType.ENTITY_NAME, false, null, null), 103 new FieldDefinition("WorkflowName", FieldType.NULL, false, null, null) 104 ); 105 106 STRING_FORM_FIELD_DEFINITIONS = List.of( 107 new FieldDefinition("CheckContentWebAddress", FieldType.NULL, false, null, null), 108 new FieldDefinition("ValidationPattern", FieldType.REGULAR_EXPRESSION, false, null, null), 109 new FieldDefinition("UpperRangeIntegerValue", FieldType.NULL, false, null, null), 110 new FieldDefinition("UpperLimitIntegerValue", FieldType.NULL, false, null, null), 111 new FieldDefinition("LowerLimitIntegerValue", FieldType.NULL, false, null, null), 112 new FieldDefinition("LowerRangeIntegerValue", FieldType.NULL, false, null, null), 113 new FieldDefinition("UpperRangeLongValue", FieldType.NULL, false, null, null), 114 new FieldDefinition("UpperLimitLongValue", FieldType.NULL, false, null, null), 115 new FieldDefinition("LowerLimitLongValue", FieldType.NULL, false, null, null), 116 new FieldDefinition("LowerRangeLongValue", FieldType.NULL, false, null, null), 117 new FieldDefinition("UnitOfMeasureKindName", FieldType.NULL, false, null, null), 118 new FieldDefinition("UnitOfMeasureTypeName", FieldType.NULL, false, null, null), 119 new FieldDefinition("EntityListItemSequenceName", FieldType.NULL, false, null, null), 120 new FieldDefinition("WorkflowName", FieldType.NULL, false, null, null) 121 ); 122 123 INTEGER_FORM_FIELD_DEFINITIONS = List.of( 124 new FieldDefinition("CheckContentWebAddress", FieldType.NULL, false, null, null), 125 new FieldDefinition("ValidationPattern", FieldType.NULL, false, null, null), 126 new FieldDefinition("UpperRangeIntegerValue", FieldType.SIGNED_INTEGER, false, null, null), 127 new FieldDefinition("UpperLimitIntegerValue", FieldType.SIGNED_INTEGER, false, null, null), 128 new FieldDefinition("LowerLimitIntegerValue", FieldType.SIGNED_INTEGER, false, null, null), 129 new FieldDefinition("LowerRangeIntegerValue", FieldType.SIGNED_INTEGER, false, null, null), 130 new FieldDefinition("UpperRangeLongValue", FieldType.NULL, false, null, null), 131 new FieldDefinition("UpperLimitLongValue", FieldType.NULL, false, null, null), 132 new FieldDefinition("LowerLimitLongValue", FieldType.NULL, false, null, null), 133 new FieldDefinition("LowerRangeLongValue", FieldType.NULL, false, null, null), 134 new FieldDefinition("UnitOfMeasureKindName", FieldType.ENTITY_NAME, false, null, null), 135 new FieldDefinition("UnitOfMeasureTypeName", FieldType.ENTITY_NAME, false, null, null), 136 new FieldDefinition("EntityListItemSequenceName", FieldType.NULL, false, null, null), 137 new FieldDefinition("WorkflowName", FieldType.NULL, false, null, null) 138 ); 139 140 LONG_FORM_FIELD_DEFINITIONS = List.of( 141 new FieldDefinition("CheckContentWebAddress", FieldType.NULL, false, null, null), 142 new FieldDefinition("ValidationPattern", FieldType.NULL, false, null, null), 143 new FieldDefinition("UpperRangeIntegerValue", FieldType.NULL, false, null, null), 144 new FieldDefinition("UpperLimitIntegerValue", FieldType.NULL, false, null, null), 145 new FieldDefinition("LowerLimitIntegerValue", FieldType.NULL, false, null, null), 146 new FieldDefinition("LowerRangeIntegerValue", FieldType.NULL, false, null, null), 147 new FieldDefinition("UpperRangeLongValue", FieldType.SIGNED_LONG, false, null, null), 148 new FieldDefinition("UpperLimitLongValue", FieldType.SIGNED_LONG, false, null, null), 149 new FieldDefinition("LowerLimitLongValue", FieldType.SIGNED_LONG, false, null, null), 150 new FieldDefinition("LowerRangeLongValue", FieldType.SIGNED_LONG, false, null, null), 151 new FieldDefinition("UnitOfMeasureKindName", FieldType.ENTITY_NAME, false, null, null), 152 new FieldDefinition("UnitOfMeasureTypeName", FieldType.ENTITY_NAME, false, null, null), 153 new FieldDefinition("EntityListItemSequenceName", FieldType.NULL, false, null, null), 154 new FieldDefinition("WorkflowName", FieldType.NULL, false, null, null) 155 ); 156 157 LISTITEM_FORM_FIELD_DEFINITIONS = List.of( 158 new FieldDefinition("CheckContentWebAddress", FieldType.NULL, false, null, null), 159 new FieldDefinition("ValidationPattern", FieldType.NULL, false, null, null), 160 new FieldDefinition("UpperRangeIntegerValue", FieldType.NULL, false, null, null), 161 new FieldDefinition("UpperLimitIntegerValue", FieldType.NULL, false, null, null), 162 new FieldDefinition("LowerLimitIntegerValue", FieldType.NULL, false, null, null), 163 new FieldDefinition("LowerRangeIntegerValue", FieldType.NULL, false, null, null), 164 new FieldDefinition("UpperRangeLongValue", FieldType.NULL, false, null, null), 165 new FieldDefinition("UpperLimitLongValue", FieldType.NULL, false, null, null), 166 new FieldDefinition("LowerLimitLongValue", FieldType.NULL, false, null, null), 167 new FieldDefinition("LowerRangeLongValue", FieldType.NULL, false, null, null), 168 new FieldDefinition("UnitOfMeasureKindName", FieldType.NULL, false, null, null), 169 new FieldDefinition("UnitOfMeasureTypeName", FieldType.NULL, false, null, null), 170 new FieldDefinition("EntityListItemSequenceName", FieldType.ENTITY_NAME, false, null, null), 171 new FieldDefinition("WorkflowName", FieldType.NULL, false, null, null) 172 ); 173 174 MULTIPLELISTITEM_FORM_FIELD_DEFINITIONS = List.of( 175 new FieldDefinition("CheckContentWebAddress", FieldType.NULL, false, null, null), 176 new FieldDefinition("ValidationPattern", FieldType.NULL, false, null, null), 177 new FieldDefinition("UpperRangeIntegerValue", FieldType.NULL, false, null, null), 178 new FieldDefinition("UpperLimitIntegerValue", FieldType.NULL, false, null, null), 179 new FieldDefinition("LowerLimitIntegerValue", FieldType.NULL, false, null, null), 180 new FieldDefinition("LowerRangeIntegerValue", FieldType.NULL, false, null, null), 181 new FieldDefinition("UpperRangeLongValue", FieldType.NULL, false, null, null), 182 new FieldDefinition("UpperLimitLongValue", FieldType.NULL, false, null, null), 183 new FieldDefinition("LowerLimitLongValue", FieldType.NULL, false, null, null), 184 new FieldDefinition("LowerRangeLongValue", FieldType.NULL, false, null, null), 185 new FieldDefinition("UnitOfMeasureKindName", FieldType.NULL, false, null, null), 186 new FieldDefinition("UnitOfMeasureTypeName", FieldType.NULL, false, null, null), 187 new FieldDefinition("EntityListItemSequenceName", FieldType.ENTITY_NAME, false, null, null), 188 new FieldDefinition("WorkflowName", FieldType.NULL, false, null, null) 189 ); 190 191 WORKFLOW_FORM_FIELD_DEFINITIONS = List.of( 192 new FieldDefinition("CheckContentWebAddress", FieldType.NULL, false, null, null), 193 new FieldDefinition("ValidationPattern", FieldType.NULL, false, null, null), 194 new FieldDefinition("UpperRangeIntegerValue", FieldType.NULL, false, null, null), 195 new FieldDefinition("UpperLimitIntegerValue", FieldType.NULL, false, null, null), 196 new FieldDefinition("LowerLimitIntegerValue", FieldType.NULL, false, null, null), 197 new FieldDefinition("LowerRangeIntegerValue", FieldType.NULL, false, null, null), 198 new FieldDefinition("UpperRangeLongValue", FieldType.NULL, false, null, null), 199 new FieldDefinition("UpperLimitLongValue", FieldType.NULL, false, null, null), 200 new FieldDefinition("LowerLimitLongValue", FieldType.NULL, false, null, null), 201 new FieldDefinition("LowerRangeLongValue", FieldType.NULL, false, null, null), 202 new FieldDefinition("UnitOfMeasureKindName", FieldType.NULL, false, null, null), 203 new FieldDefinition("UnitOfMeasureTypeName", FieldType.NULL, false, null, null), 204 new FieldDefinition("EntityListItemSequenceName", FieldType.NULL, false, null, null), 205 new FieldDefinition("WorkflowName", FieldType.ENTITY_NAME, true, null, null) 206 ); 207 208 OTHER_FORM_FIELD_DEFINITIONS = List.of( 209 new FieldDefinition("CheckContentWebAddress", FieldType.NULL, false, null, null), 210 new FieldDefinition("ValidationPattern", FieldType.NULL, false, null, null), 211 new FieldDefinition("UpperRangeIntegerValue", FieldType.NULL, false, null, null), 212 new FieldDefinition("UpperLimitIntegerValue", FieldType.NULL, false, null, null), 213 new FieldDefinition("LowerLimitIntegerValue", FieldType.NULL, false, null, null), 214 new FieldDefinition("LowerRangeIntegerValue", FieldType.NULL, false, null, null), 215 new FieldDefinition("UpperRangeLongValue", FieldType.NULL, false, null, null), 216 new FieldDefinition("UpperLimitLongValue", FieldType.NULL, false, null, null), 217 new FieldDefinition("LowerLimitLongValue", FieldType.NULL, false, null, null), 218 new FieldDefinition("LowerRangeLongValue", FieldType.NULL, false, null, null), 219 new FieldDefinition("UnitOfMeasureKindName", FieldType.NULL, false, null, null), 220 new FieldDefinition("UnitOfMeasureTypeName", FieldType.NULL, false, null, null), 221 new FieldDefinition("EntityListItemSequenceName", FieldType.NULL, false, null, null), 222 new FieldDefinition("WorkflowName", FieldType.NULL, false, null, null) 223 ); 224 } 225 226 @Inject 227 SequenceControl sequenceControl; 228 229 @Inject 230 EntityAttributeLogic entityAttributeLogic; 231 232 @Inject 233 EntityTypeLogic entityTypeLogic; 234 235 @Inject 236 SequenceTypeLogic sequenceTypeLogic; 237 238 @Inject 239 UnitOfMeasureTypeLogic unitOfMeasureTypeLogic; 240 241 @Inject 242 WorkflowLogic workflowLogic; 243 244 /** Creates a new instance of CreateEntityAttributeCommand */ 245 public CreateEntityAttributeCommand() { 246 super(COMMAND_SECURITY_DEFINITION, FORM_FIELD_DEFINITIONS, false); 247 } 248 249 public static ValidationResult AdditionalEntityAttributeValidation(EntityAttributeEdit edit, Validator validator, 250 EntityAttributeType entityAttributeType) { 251 return switch(EntityAttributeTypes.valueOf(entityAttributeType.getEntityAttributeTypeName())) { 252 case BLOB -> validator.validate(edit, BLOB_FORM_FIELD_DEFINITIONS); 253 case STRING -> validator.validate(edit, STRING_FORM_FIELD_DEFINITIONS); 254 case INTEGER -> validator.validate(edit, INTEGER_FORM_FIELD_DEFINITIONS); 255 case LONG -> validator.validate(edit, LONG_FORM_FIELD_DEFINITIONS); 256 case LISTITEM -> validator.validate(edit, LISTITEM_FORM_FIELD_DEFINITIONS); 257 case MULTIPLELISTITEM -> validator.validate(edit, MULTIPLELISTITEM_FORM_FIELD_DEFINITIONS); 258 case WORKFLOW -> validator.validate(edit, WORKFLOW_FORM_FIELD_DEFINITIONS); 259 default -> validator.validate(edit, OTHER_FORM_FIELD_DEFINITIONS); 260 }; 261 } 262 263 @Override 264 protected ValidationResult validate() { 265 var validator = new Validator(this); 266 var validationResult = validator.validate(form, FORM_FIELD_DEFINITIONS); 267 268 if(!validationResult.getHasErrors()) { 269 var entityAttributeType = entityAttributeLogic.getEntityAttributeTypeByName(this, form.getEntityAttributeTypeName()); 270 271 if(!hasExecutionErrors()) { 272 validationResult = AdditionalEntityAttributeValidation(form, validator, entityAttributeType); 273 } 274 } 275 276 return validationResult; 277 } 278 279 @Override 280 protected BaseResult execute() { 281 var result = CoreResultFactory.getCreateEntityAttributeResult(); 282 EntityAttribute entityAttribute = null; 283 284 if(!hasExecutionErrors()) { 285 var unitOfMeasureKindName = form.getUnitOfMeasureKindName(); 286 var unitOfMeasureTypeName = form.getUnitOfMeasureTypeName(); 287 var parameterCount = (unitOfMeasureKindName == null ? 0 : 1) + (unitOfMeasureTypeName == null ? 0 : 1); 288 289 if(parameterCount == 0 || parameterCount == 2) { 290 var entityType = entityTypeLogic.getEntityTypeByUniversalSpec(this, form); 291 var entityAttributeTypeName = form.getEntityAttributeTypeName(); 292 var entityAttributeType = entityAttributeLogic.getEntityAttributeTypeByName(this, entityAttributeTypeName); 293 294 if(!hasExecutionErrors()) { 295 var entityListItemSequenceName = entityAttributeTypeName.equals(EntityAttributeTypes.LISTITEM.name()) 296 || entityAttributeTypeName.equals(EntityAttributeTypes.MULTIPLELISTITEM.name()) ? 297 form.getEntityListItemSequenceName() : null; 298 Sequence entityListItemSequence = null; 299 300 if(entityListItemSequenceName != null) { 301 var sequenceType = sequenceTypeLogic.getSequenceTypeByName(this, SequenceTypes.ENTITY_LIST_ITEM.name()); 302 303 if(!hasExecutionErrors()) { 304 entityListItemSequence = sequenceControl.getSequenceByName(sequenceType, entityListItemSequenceName); 305 306 if(entityListItemSequence == null) { 307 addExecutionError(ExecutionErrors.UnknownEntityListItemSequenceName.name(), entityListItemSequenceName); 308 } 309 } 310 } 311 312 if(!hasExecutionErrors()) { 313 UnitOfMeasureType unitOfMeasureType = null; 314 315 if(parameterCount == 2) { 316 unitOfMeasureType = unitOfMeasureTypeLogic.getUnitOfMeasureTypeByName(this, unitOfMeasureKindName, unitOfMeasureTypeName); 317 } 318 319 if(!hasExecutionErrors()) { 320 var workflowName = form.getWorkflowName(); 321 Workflow workflow = null; 322 323 if(workflowName != null) { 324 workflow = workflowLogic.getWorkflowByName(this, workflowName); 325 } 326 327 if(!hasExecutionErrors()) { 328 var partyPK = getPartyPK(); 329 var entityAttributeName = form.getEntityAttributeName(); 330 var trackRevisions = Boolean.valueOf(form.getTrackRevisions()); 331 var strCheckContentWebAddress = form.getCheckContentWebAddress(); 332 var checkContentWebAddress = strCheckContentWebAddress == null ? null : Boolean.valueOf(strCheckContentWebAddress); 333 var validationPattern = form.getValidationPattern(); 334 var strUpperRangeIntegerValue = form.getUpperRangeIntegerValue(); 335 var upperRangeIntegerValue = strUpperRangeIntegerValue == null ? null : Integer.valueOf(strUpperRangeIntegerValue); 336 var strUpperLimitIntegerValue = form.getUpperLimitIntegerValue(); 337 var upperLimitIntegerValue = strUpperLimitIntegerValue == null ? null : Integer.valueOf(strUpperLimitIntegerValue); 338 var strLowerLimitIntegerValue = form.getLowerLimitIntegerValue(); 339 var lowerLimitIntegerValue = strLowerLimitIntegerValue == null ? null : Integer.valueOf(strLowerLimitIntegerValue); 340 var strLowerRangeIntegerValue = form.getLowerRangeIntegerValue(); 341 var lowerRangeIntegerValue = strLowerRangeIntegerValue == null ? null : Integer.valueOf(strLowerRangeIntegerValue); 342 var strUpperRangeLongValue = form.getUpperRangeLongValue(); 343 var upperRangeLongValue = strUpperRangeLongValue == null ? null : Long.valueOf(strUpperRangeLongValue); 344 var strUpperLimitLongValue = form.getUpperLimitLongValue(); 345 var upperLimitLongValue = strUpperLimitLongValue == null ? null : Long.valueOf(strUpperLimitLongValue); 346 var strLowerLimitLongValue = form.getLowerLimitLongValue(); 347 var lowerLimitLongValue = strLowerLimitLongValue == null ? null : Long.valueOf(strLowerLimitLongValue); 348 var strLowerRangeLongValue = form.getLowerRangeLongValue(); 349 var lowerRangeLongValue = strLowerRangeLongValue == null ? null : Long.valueOf(strLowerRangeLongValue); 350 var sortOrder = Integer.valueOf(form.getSortOrder()); 351 var description = form.getDescription(); 352 353 entityAttribute = entityAttributeLogic.createEntityAttribute(this, entityType, 354 entityAttributeName, entityAttributeType, trackRevisions, checkContentWebAddress, 355 validationPattern, upperRangeIntegerValue, upperLimitIntegerValue, lowerLimitIntegerValue, 356 lowerRangeIntegerValue, upperRangeLongValue, upperLimitLongValue, lowerLimitLongValue, 357 lowerRangeLongValue, entityListItemSequence, unitOfMeasureType, workflow, sortOrder, 358 partyPK, getPreferredLanguage(), description); 359 } 360 } 361 } 362 } 363 } else { 364 addExecutionError(ExecutionErrors.InvalidParameterCount.name()); 365 } 366 } 367 368 if(entityAttribute != null) { 369 var basePK = entityAttribute.getPrimaryKey(); 370 371 result.setEntityAttributeName(entityAttribute.getLastDetail().getEntityAttributeName()); 372 result.setEntityRef(basePK.getEntityRef()); 373 } 374 375 return result; 376 } 377 378}