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.impexp;
  
  import org.apache.atlas.exception.AtlasBaseException;
  import org.apache.atlas.glossary.GlossaryService;
  import org.apache.atlas.model.TypeCategory;
  import org.apache.atlas.model.glossary.AtlasGlossaryTerm;
  import org.apache.atlas.model.instance.AtlasClassification;
  import org.apache.atlas.model.instance.AtlasEntity;
  import org.apache.atlas.model.instance.AtlasRelatedObjectId;
  import org.apache.atlas.model.typedef.AtlasStructDef;
  import org.apache.atlas.type.AtlasArrayType;
  import org.apache.atlas.type.AtlasBusinessMetadataType;
  import org.apache.atlas.type.AtlasClassificationType;
  import org.apache.atlas.type.AtlasEntityType;
  import org.apache.atlas.type.AtlasEnumType;
  import org.apache.atlas.type.AtlasMapType;
  import org.apache.atlas.type.AtlasRelationshipType;
  import org.apache.atlas.type.AtlasStructType;
  import org.apache.atlas.type.AtlasType;
  import org.apache.atlas.type.AtlasTypeRegistry;
  import org.apache.commons.collections.CollectionUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.util.List;
  import java.util.Map;
  
  class ExportTypeProcessor {
      private static final Logger LOG = LoggerFactory.getLogger(ExportTypeProcessor.class);
  
      private static final String RELATIONSHIP_ATTR_MEANINGS = "meanings";
  
      private final AtlasTypeRegistry typeRegistry;
      private final GlossaryService   glossaryService;
  
      ExportTypeProcessor(AtlasTypeRegistry typeRegistry, GlossaryService glossaryService) {
          this.typeRegistry    = typeRegistry;
          this.glossaryService = glossaryService;
      }
  
      public void addTypes(AtlasEntity entity, ExportService.ExportContext context) {
          addEntityType(entity.getTypeName(), context);
  
          if (CollectionUtils.isNotEmpty(entity.getClassifications())) {
              for (AtlasClassification c : entity.getClassifications()) {
                  addClassificationType(c.getTypeName(), context);
              }
          }
  
          addTerms(entity, context);
      }
  
      private void addTerms(AtlasEntity entity, ExportService.ExportContext context) {
          Object relAttrMeanings = entity.getRelationshipAttribute(RELATIONSHIP_ATTR_MEANINGS);
          if (!(relAttrMeanings instanceof List)) {
              return;
          }
  
          List<?> list = (List<?>) relAttrMeanings;
  
          if (CollectionUtils.isEmpty(list)) {
              return;
          }
  
          for (Object objectId : list) {
              if (objectId instanceof AtlasRelatedObjectId) {
                  AtlasRelatedObjectId termObjectId = (AtlasRelatedObjectId) objectId;
  
                  try {
                      AtlasGlossaryTerm term = glossaryService.getTerm(termObjectId.getGuid());
                      context.termsGlossary.put(termObjectId.getGuid(), term.getAnchor().getGlossaryGuid());
                  } catch (AtlasBaseException e) {
                      LOG.warn("Error fetching term details: {}", termObjectId);
                  }
              }
          }
      }
  
      private void addType(String typeName, ExportService.ExportContext context) {
          AtlasType type = null;
  
          try {
              type = typeRegistry.getType(typeName);
  
              addType(type, context);
          } catch (AtlasBaseException excp) {
              LOG.error("unknown type {}", typeName);
          }
      }
  
      private void addEntityType(String typeName, ExportService.ExportContext context) {
          if (!context.entityTypes.contains(typeName)) {
              AtlasEntityType entityType = typeRegistry.getEntityTypeByName(typeName);
  
              addEntityType(entityType, context);
          }
      }
  
      private void addClassificationType(String typeName, ExportService.ExportContext context) {
          if (!context.classificationTypes.contains(typeName)) {
              AtlasClassificationType classificationType = typeRegistry.getClassificationTypeByName(typeName);
  
              addClassificationType(classificationType, context);
          }
      }
  
      private void addType(AtlasType type, ExportService.ExportContext context) {
          if (type.getTypeCategory() == TypeCategory.PRIMITIVE) {
              return;
          }
  
          if (type instanceof AtlasArrayType) {
              AtlasArrayType arrayType = (AtlasArrayType) type;
  
              addType(arrayType.getElementType(), context);
          } else if (type instanceof AtlasMapType) {
              AtlasMapType mapType = (AtlasMapType) type;
  
              addType(mapType.getKeyType(), context);
              addType(mapType.getValueType(), context);
          } else if (type instanceof AtlasEntityType) {
              addEntityType((AtlasEntityType) type, context);
          } else if (type instanceof AtlasClassificationType) {
              addClassificationType((AtlasClassificationType) type, context);
          } else if (type instanceof AtlasRelationshipType) {
              addRelationshipType(type.getTypeName(), context);
          } else if (type instanceof AtlasBusinessMetadataType) {
              addBusinessMetadataType((AtlasBusinessMetadataType) type, context);
          } else if (type instanceof AtlasStructType) {
              addStructType((AtlasStructType) type, context);
          } else if (type instanceof AtlasEnumType) {
              addEnumType((AtlasEnumType) type, context);
          }
      }
  
      private void addEntityType(AtlasEntityType entityType, ExportService.ExportContext context) {
          if (!context.entityTypes.contains(entityType.getTypeName())) {
              context.entityTypes.add(entityType.getTypeName());
  
              addAttributeTypes(entityType, context);
              addRelationshipTypes(entityType, context);
              addBusinessMetadataType(entityType, context);
  
              if (CollectionUtils.isNotEmpty(entityType.getAllSuperTypes())) {
                  for (String superType : entityType.getAllSuperTypes()) {
                      addEntityType(superType, context);
                  }
              }
          }
      }
  
      private void addClassificationType(AtlasClassificationType classificationType, ExportService.ExportContext context) {
          if (!context.classificationTypes.contains(classificationType.getTypeName())) {
              context.classificationTypes.add(classificationType.getTypeName());
  
              addAttributeTypes(classificationType, context);
  
              if (CollectionUtils.isNotEmpty(classificationType.getAllSuperTypes())) {
                  for (String superType : classificationType.getAllSuperTypes()) {
                      addClassificationType(superType, context);
                  }
              }
          }
      }
  
      private void addStructType(AtlasStructType structType, ExportService.ExportContext context) {
          if (!context.structTypes.contains(structType.getTypeName())) {
              context.structTypes.add(structType.getTypeName());
  
              addAttributeTypes(structType, context);
          }
      }
  
      private void addEnumType(AtlasEnumType enumType, ExportService.ExportContext context) {
          if (!context.enumTypes.contains(enumType.getTypeName())) {
              context.enumTypes.add(enumType.getTypeName());
          }
      }
  
      private void addRelationshipType(String relationshipTypeName, ExportService.ExportContext context) {
          if (!context.relationshipTypes.contains(relationshipTypeName)) {
              AtlasRelationshipType relationshipType = typeRegistry.getRelationshipTypeByName(relationshipTypeName);
  
              if (relationshipType != null) {
                  context.relationshipTypes.add(relationshipTypeName);
  
                  addAttributeTypes(relationshipType, context);
                  addEntityType(relationshipType.getEnd1Type(), context);
                  addEntityType(relationshipType.getEnd2Type(), context);
              }
          }
      }
  
      private void addBusinessMetadataType(AtlasBusinessMetadataType businessMetadataType, ExportService.ExportContext context) {
          if (!context.businessMetadataTypes.contains(businessMetadataType.getTypeName())) {
              context.businessMetadataTypes.add(businessMetadataType.getTypeName());
  
              addAttributeTypes(businessMetadataType, context);
          }
      }
  
      private void addBusinessMetadataType(AtlasEntityType entityType, ExportService.ExportContext context) {
          for (String bmTypeName : entityType.getBusinessAttributes().keySet()) {
              AtlasBusinessMetadataType bmType = typeRegistry.getBusinessMetadataTypeByName(bmTypeName);
  
              addBusinessMetadataType(bmType, context);
          }
      }
  
      private void addAttributeTypes(AtlasStructType structType, ExportService.ExportContext context) {
          for (AtlasStructDef.AtlasAttributeDef attributeDef : structType.getStructDef().getAttributeDefs()) {
              addType(attributeDef.getTypeName(), context);
          }
      }
  
      private void addRelationshipTypes(AtlasEntityType entityType, ExportService.ExportContext context) {
          for (Map.Entry<String, Map<String, AtlasStructType.AtlasAttribute>> entry : entityType.getRelationshipAttributes().entrySet()) {
              for (String relationshipType : entry.getValue().keySet()) {
                  addRelationshipType(relationshipType, context);
              }
          }
      }
  }
  