content: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you 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
   * <p>
   * http://www.apache.org/licenses/LICENSE-2.0
   * <p>
   * 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.
   */
  
  package org.apache.atlas.repository.converters;
  
  import org.apache.atlas.exception.AtlasBaseException;
  import org.apache.atlas.model.typedef.AtlasClassificationDef;
  import org.apache.atlas.model.typedef.AtlasEntityDef;
  import org.apache.atlas.model.typedef.AtlasEnumDef;
  import org.apache.atlas.model.typedef.AtlasEnumDef.AtlasEnumElementDef;
  import org.apache.atlas.model.typedef.AtlasStructDef;
  import org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef;
  import org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef.Cardinality;
  import org.apache.atlas.model.typedef.AtlasStructDef.AtlasConstraintDef;
  import org.apache.atlas.model.typedef.AtlasTypeDefHeader;
  import org.apache.atlas.model.typedef.AtlasTypesDef;
  import org.apache.atlas.repository.store.graph.v2.AtlasStructDefStoreV2;
  import org.apache.atlas.type.AtlasClassificationType;
  import org.apache.atlas.type.AtlasEntityType;
  import org.apache.atlas.type.AtlasEnumType;
  import org.apache.atlas.type.AtlasStructType;
  import org.apache.atlas.type.AtlasStructType.AtlasAttribute;
  import org.apache.atlas.type.AtlasType;
  import org.apache.atlas.type.AtlasTypeRegistry;
  import org.apache.atlas.type.AtlasTypeUtil;
  import org.apache.atlas.v1.model.typedef.AttributeDefinition;
  import org.apache.atlas.v1.model.typedef.ClassTypeDefinition;
  import org.apache.atlas.v1.model.typedef.EnumTypeDefinition;
  import org.apache.atlas.v1.model.typedef.EnumTypeDefinition.EnumValue;
  import org.apache.atlas.v1.model.typedef.Multiplicity;
  import org.apache.atlas.v1.model.typedef.StructTypeDefinition;
  import org.apache.atlas.v1.model.typedef.TraitTypeDefinition;
  import org.apache.atlas.v1.model.typedef.TypesDef;
  import org.apache.commons.collections.CollectionUtils;
  import org.apache.commons.lang.StringUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Set;
  
  import static org.apache.atlas.AtlasErrorCode.INVALID_TYPE_DEFINITION;
  import static org.apache.atlas.model.typedef.AtlasStructDef.AtlasConstraintDef.CONSTRAINT_PARAM_ATTRIBUTE;
  import static org.apache.atlas.model.typedef.AtlasStructDef.AtlasConstraintDef.CONSTRAINT_TYPE_INVERSE_REF;
  import static org.apache.atlas.model.typedef.AtlasStructDef.AtlasConstraintDef.CONSTRAINT_TYPE_OWNED_REF;
  import static org.apache.atlas.type.AtlasTypeUtil.isArrayType;
  
  public final class TypeConverterUtil {
      private static final Logger LOG = LoggerFactory.getLogger(TypeConverterUtil.class);
  
      private TypeConverterUtil() {}
  
      public static TypesDef toTypesDef(AtlasType type, AtlasTypeRegistry typeRegistry) throws AtlasBaseException {
          final TypesDef ret;
  
          if (type instanceof AtlasEnumType) {
              ret = TypeConverterUtil.enumToTypesDef((AtlasEnumType) type);
          } else if (type instanceof AtlasEntityType) {
              ret = TypeConverterUtil.entityToTypesDef((AtlasEntityType) type, typeRegistry);
          } else if (type instanceof AtlasClassificationType) {
              ret = TypeConverterUtil.classificationToTypesDef((AtlasClassificationType) type, typeRegistry);
          } else if (type instanceof AtlasStructType) {
              ret = TypeConverterUtil.structToTypesDef((AtlasStructType) type, typeRegistry);
          } else {
              ret = new TypesDef();
          }
  
          return ret;
      }
  
      public static AtlasTypesDef toAtlasTypesDef(String typeDefinition, AtlasTypeRegistry registry) throws AtlasBaseException {
          AtlasTypesDef ret = new AtlasTypesDef();
  
          try {
              if (StringUtils.isEmpty(typeDefinition)) {
                  throw new AtlasBaseException(INVALID_TYPE_DEFINITION, typeDefinition);
              }
  
              TypesDef typesDef = AtlasType.fromV1Json(typeDefinition, TypesDef.class);
              if (CollectionUtils.isNotEmpty(typesDef.getEnumTypes())) {
                  List<AtlasEnumDef> enumDefs = toAtlasEnumDefs(typesDef.getEnumTypes());
  
                  ret.setEnumDefs(enumDefs);
              }
  
              if (CollectionUtils.isNotEmpty(typesDef.getStructTypes())) {
                  List<AtlasStructDef> structDefs = toAtlasStructDefs(typesDef.getStructTypes());
  
                  ret.setStructDefs(structDefs);
              }
  
              if (CollectionUtils.isNotEmpty(typesDef.getClassTypes())) {
                  List<AtlasEntityDef> entityDefs = toAtlasEntityDefs(typesDef.getClassTypes(), registry);
  
                  ret.setEntityDefs(entityDefs);
              }
  
              if (CollectionUtils.isNotEmpty(typesDef.getTraitTypes())) {
                  List<AtlasClassificationDef> classificationDefs = toAtlasClassificationDefs(typesDef.getTraitTypes());
  
                  ret.setClassificationDefs(classificationDefs);
              }
          } catch (Exception e) {
              LOG.error("Invalid type definition = {}", typeDefinition, e);
  
              throw new AtlasBaseException(INVALID_TYPE_DEFINITION, typeDefinition);
          }
  
          return ret;
      }
  
      public static List<String> getTypeNames(List<AtlasTypeDefHeader> atlasTypesDefs) {
          List<String> ret = new ArrayList<>();
  
          if (CollectionUtils.isNotEmpty(atlasTypesDefs)) {
              for (AtlasTypeDefHeader atlasTypesDef : atlasTypesDefs) {
                  ret.add(atlasTypesDef.getName());
              }
          }
  
          return ret;
      }
  
      public static List<String> getTypeNames(AtlasTypesDef typesDef) {
          List<AtlasTypeDefHeader> atlasTypesDefs = AtlasTypeUtil.toTypeDefHeader(typesDef);
  
          return getTypeNames(atlasTypesDefs);
      }
  
      public static AtlasAttributeDef toAtlasAttributeDef(final AttributeDefinition attrDefinition) {
          AtlasAttributeDef ret = new AtlasAttributeDef(attrDefinition.getName(), attrDefinition.getDataTypeName(), attrDefinition.getSearchWeight(), attrDefinition.getIndexType());
  
          ret.setIsIndexable(attrDefinition.getIsIndexable());
          ret.setIsUnique(attrDefinition.getIsUnique());
  
          if (attrDefinition.getIsComposite()) {
              ret.addConstraint(new AtlasConstraintDef(CONSTRAINT_TYPE_OWNED_REF));
          }
  
          if (StringUtils.isNotBlank(attrDefinition.getReverseAttributeName())) {
              ret.addConstraint(new AtlasConstraintDef(CONSTRAINT_TYPE_INVERSE_REF, Collections.singletonMap(CONSTRAINT_PARAM_ATTRIBUTE, attrDefinition.getReverseAttributeName())));
          }
  
          // Multiplicity attribute mapping
          Multiplicity multiplicity = attrDefinition.getMultiplicity();
          int          minCount     = multiplicity.getLower();
          int          maxCount     = multiplicity.getUpper();
          boolean      isUnique     = multiplicity.getIsUnique();
  
          if (minCount == 0) {
              ret.setIsOptional(true);
              ret.setValuesMinCount(0);
          } else {
              ret.setIsOptional(false);
              ret.setValuesMinCount(minCount);
          }
  
          if (maxCount < 2) {
              ret.setCardinality(Cardinality.SINGLE);
              ret.setValuesMaxCount(1);
          } else {
              if (!isUnique) {
                  ret.setCardinality(Cardinality.LIST);
              } else {
                  ret.setCardinality(Cardinality.SET);
              }
  
              ret.setValuesMaxCount(maxCount);
          }
  
          return ret;
      }
  
      private static TypesDef enumToTypesDef(AtlasEnumType enumType) {
          AtlasEnumDef enumDef = enumType.getEnumDef();
  
          String          enumName    = enumDef.getName();
          String          enumDesc    = enumDef.getDescription();
          String          enumVersion = enumDef.getTypeVersion();
          List<EnumValue> enumValues  = getEnumValues(enumDef.getElementDefs());
  
          EnumTypeDefinition enumTypeDef = new EnumTypeDefinition(enumName, enumDesc, enumVersion, enumValues);
  
          return new TypesDef(Collections.singletonList(enumTypeDef), null, null, null);
      }
  
      private static TypesDef structToTypesDef(AtlasStructType structType, AtlasTypeRegistry registry) {
          String                    typeName    = structType.getStructDef().getName();
          String                    typeDesc    = structType.getStructDef().getDescription();
          String                    typeVersion = structType.getStructDef().getTypeVersion();
          List<AttributeDefinition> attributes  = getAttributes(structType, registry);
  
          StructTypeDefinition structTypeDef = new StructTypeDefinition(typeName, typeDesc, typeVersion, attributes);
  
          return new TypesDef(null, Collections.singletonList(structTypeDef), null, null);
      }
  
      private static TypesDef entityToTypesDef(AtlasEntityType entityType, AtlasTypeRegistry registry) {
          String                    typeName    = entityType.getEntityDef().getName();
          String                    typeDesc    = entityType.getEntityDef().getDescription();
          String                    typeVersion = entityType.getEntityDef().getTypeVersion();
          Set<String>               superTypes  = entityType.getEntityDef().getSuperTypes();
          List<AttributeDefinition> attributes  = getAttributes(entityType, registry);
  
          ClassTypeDefinition classTypeDef = new ClassTypeDefinition(typeName, typeDesc, typeVersion, attributes, superTypes);
  
          return new TypesDef(null, null, null, Collections.singletonList(classTypeDef));
      }
  
      private static TypesDef classificationToTypesDef(AtlasClassificationType classificationType, AtlasTypeRegistry registry) {
          String                    typeName    = classificationType.getClassificationDef().getName();
          String                    typeDesc    = classificationType.getClassificationDef().getDescription();
          String                    typeVersion = classificationType.getClassificationDef().getTypeVersion();
          Set<String>               superTypes  = new HashSet<>(classificationType.getClassificationDef().getSuperTypes());
          List<AttributeDefinition> attributes  = getAttributes(classificationType, registry);
  
          TraitTypeDefinition traitTypeDef = new TraitTypeDefinition(typeName, typeDesc, typeVersion, attributes, superTypes);
  
          return new TypesDef(null, null, Collections.singletonList(traitTypeDef), null);
      }
  
      private static List<AtlasEnumDef> toAtlasEnumDefs(List<EnumTypeDefinition> enumTypeDefinitions) {
          List<AtlasEnumDef> ret = new ArrayList<>();
  
          for (EnumTypeDefinition enumType : enumTypeDefinitions) {
              AtlasEnumDef enumDef = new AtlasEnumDef();
  
              enumDef.setName(enumType.getName());
              enumDef.setDescription(enumType.getDescription());
              enumDef.setTypeVersion(enumType.getVersion());
              enumDef.setElementDefs(getAtlasEnumElementDefs(enumType.getEnumValues()));
  
              ret.add(enumDef);
          }
  
          return ret;
      }
  
      private static List<AtlasStructDef> toAtlasStructDefs(List<StructTypeDefinition> structTypeDefinitions) {
          List<AtlasStructDef> ret = new ArrayList<>();
  
          for (StructTypeDefinition structType : structTypeDefinitions) {
              List<AtlasAttributeDef> attrDefs = new ArrayList<>();
  
              if (CollectionUtils.isNotEmpty(structType.getAttributeDefinitions())) {
                  for (AttributeDefinition attrDefinition : structType.getAttributeDefinitions()) {
                      attrDefs.add(toAtlasAttributeDef(attrDefinition));
                  }
              }
  
              AtlasStructDef structDef = new AtlasStructDef(structType.getTypeName(), structType.getTypeDescription(), structType.getTypeVersion(), attrDefs);
  
              ret.add(structDef);
          }
  
          return ret;
      }
  
      private static List<AtlasClassificationDef> toAtlasClassificationDefs(List<TraitTypeDefinition> traitTypeDefinitions) {
          List<AtlasClassificationDef> ret = new ArrayList<>();
  
          for (TraitTypeDefinition traitType : traitTypeDefinitions) {
              List<AtlasAttributeDef> attrDefs = new ArrayList<>();
  
              if (CollectionUtils.isNotEmpty(traitType.getAttributeDefinitions())) {
                  for (AttributeDefinition attrDefinition : traitType.getAttributeDefinitions()) {
                      attrDefs.add(toAtlasAttributeDef(attrDefinition));
                  }
              }
  
              AtlasClassificationDef classifDef = new AtlasClassificationDef(traitType.getTypeName(), traitType.getTypeDescription(), traitType.getTypeVersion(), attrDefs, traitType.getSuperTypes());
  
              ret.add(classifDef);
          }
  
          return ret;
      }
  
      private static List<AtlasEntityDef> toAtlasEntityDefs(List<ClassTypeDefinition> classTypeDefinitions, AtlasTypeRegistry registry) {
          List<AtlasEntityDef> ret = new ArrayList<>();
  
          for (ClassTypeDefinition classType : classTypeDefinitions) {
              List<AtlasAttributeDef> attrDefs = new ArrayList<>();
  
              if (CollectionUtils.isNotEmpty(classType.getAttributeDefinitions())) {
                  for (AttributeDefinition oldAttr : classType.getAttributeDefinitions()) {
                      AtlasAttributeDef newAttr = toAtlasAttributeDef(oldAttr);
  
                      attrDefs.add(newAttr);
                  }
              }
  
              AtlasEntityDef entityDef = new AtlasEntityDef(classType.getTypeName(), classType.getTypeDescription(), classType.getTypeVersion(), attrDefs, classType.getSuperTypes());
  
              ret.add(entityDef);
          }
  
          return ret;
      }
  
      private static String getArrayTypeName(String attrType) {
          String ret = null;
  
          if (isArrayType(attrType)) {
              Set<String> typeNames = AtlasTypeUtil.getReferencedTypeNames(attrType);
  
              if (!typeNames.isEmpty()) {
                  ret = typeNames.iterator().next();
              }
          }
  
          return ret;
      }
  
      private static List<AtlasEnumElementDef> getAtlasEnumElementDefs(List<EnumValue> enums) {
          List<AtlasEnumElementDef> ret = new ArrayList<>();
  
          for (EnumValue enumElem : enums) {
              ret.add(new AtlasEnumElementDef(enumElem.getValue(), null, enumElem.getOrdinal()));
          }
  
          return ret;
      }
  
      private static List<EnumValue> getEnumValues(List<AtlasEnumElementDef> enumDefs) {
          List<EnumValue> ret = new ArrayList<>();
  
          if (CollectionUtils.isNotEmpty(enumDefs)) {
              for (AtlasEnumElementDef enumDef : enumDefs) {
                  if (enumDef != null) {
                      ret.add(new EnumValue(enumDef.getValue(), enumDef.getOrdinal()));
                  }
              }
          }
  
          return ret;
      }
  
      private static List<AttributeDefinition> getAttributes(AtlasStructType structType, AtlasTypeRegistry registry) {
          List<AttributeDefinition> ret      = new ArrayList<>();
          List<AtlasAttributeDef>   attrDefs = structType.getStructDef().getAttributeDefs();
  
          if (CollectionUtils.isNotEmpty(attrDefs)) {
              for (AtlasAttributeDef attrDef : attrDefs) {
                  AtlasAttribute attribute = structType.getAttribute(attrDef.getName());
  
                  AttributeDefinition oldAttrDef = AtlasStructDefStoreV2.toAttributeDefinition(attribute);
  
                  ret.add(new AttributeDefinition(oldAttrDef.getName(),
                          oldAttrDef.getDataTypeName(),
                          new Multiplicity(oldAttrDef.getMultiplicity()),
                          oldAttrDef.getIsComposite(),
                          oldAttrDef.getIsUnique(),
                          oldAttrDef.getIsIndexable(),
                          oldAttrDef.getReverseAttributeName(),
                          oldAttrDef.getOptions(),
                          oldAttrDef.getSearchWeight(),
                          oldAttrDef.getIndexType()));
              }
          }
  
          return ret;
      }
  }
  