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.type;
  
  import org.apache.atlas.AtlasErrorCode;
  import org.apache.atlas.exception.AtlasBaseException;
  import org.apache.atlas.model.instance.AtlasStruct;
  import org.apache.atlas.model.typedef.AtlasBusinessMetadataDef;
  import org.apache.atlas.model.typedef.AtlasStructDef.AtlasAttributeDef;
  import org.apache.commons.collections.CollectionUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.lang.reflect.Array;
  import java.util.Collection;
  import java.util.Collections;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  
  import static org.apache.atlas.model.typedef.AtlasBusinessMetadataDef.ATTR_MAX_STRING_LENGTH;
  import static org.apache.atlas.model.typedef.AtlasBusinessMetadataDef.ATTR_OPTION_APPLICABLE_ENTITY_TYPES;
  import static org.apache.atlas.model.typedef.AtlasBusinessMetadataDef.ATTR_VALID_PATTERN;
  
  public class AtlasBusinessMetadataType extends AtlasStructType {
      private static final Logger LOG = LoggerFactory.getLogger(AtlasBusinessMetadataType.class);
  
      private final AtlasBusinessMetadataDef businessMetadataDef;
  
      public AtlasBusinessMetadataType(AtlasBusinessMetadataDef businessMetadataDef) {
          super(businessMetadataDef);
  
          this.businessMetadataDef = businessMetadataDef;
      }
  
      @Override
      public AtlasStruct createDefaultValue() {
          return null;  // there is no runtime instance for businessMetadataDef, so return null
      }
  
      @Override
      public boolean isValidValue(Object o) {
          return true; // there is no runtime instance for businessMetadataDef, so return true
      }
  
      @Override
      public Object getNormalizedValue(Object a) {
          return null;  // there is no runtime instance for businessMetadataDef, so return null
      }
  
      @Override
      void resolveReferences(AtlasTypeRegistry typeRegistry) throws AtlasBaseException {
          super.resolveReferences(typeRegistry);
  
          Map<String, AtlasBusinessAttribute> a = new HashMap<>();
  
          for (AtlasAttribute attribute : super.allAttributes.values()) {
              AtlasAttributeDef attributeDef = attribute.getAttributeDef();
              String            attrName     = attribute.getName();
              AtlasType         attrType     = attribute.getAttributeType();
  
              if (attrType instanceof AtlasArrayType) {
                  attrType = ((AtlasArrayType) attrType).getElementType();
              } else if (attrType instanceof AtlasMapType) {
                  attrType = ((AtlasMapType) attrType).getValueType();
              }
  
              // check if attribute type is not struct/classification/entity/business-metadata
              if (attrType instanceof AtlasStructType) {
                  throw new AtlasBaseException(AtlasErrorCode.BUSINESS_METADATA_DEF_ATTRIBUTE_TYPE_INVALID, getTypeName(), attrName);
              }
  
              Set<String>          entityTypeNames = attribute.getOptionSet(ATTR_OPTION_APPLICABLE_ENTITY_TYPES);
              Set<AtlasEntityType> entityTypes     = new HashSet<>();
  
              if (CollectionUtils.isNotEmpty(entityTypeNames)) {
                  for (String entityTypeName : entityTypeNames) {
                      AtlasEntityType entityType = typeRegistry.getEntityTypeByName(entityTypeName);
  
                      if (entityType == null) {
                          throw new AtlasBaseException(AtlasErrorCode.TYPE_NAME_NOT_FOUND, entityTypeName);
                      }
  
                      entityTypes.add(entityType);
                  }
              }
  
              AtlasBusinessAttribute bmAttribute;
              if (attrType instanceof AtlasBuiltInTypes.AtlasStringType) {
                  Integer maxStringLength = attribute.getOptionInt(ATTR_MAX_STRING_LENGTH);
                  if (maxStringLength == null) {
                      throw new AtlasBaseException(AtlasErrorCode.MISSING_MANDATORY_ATTRIBUTE, attributeDef.getName(), "options." + ATTR_MAX_STRING_LENGTH);
                  }
  
                  String validPattern = attribute.getOptionString(ATTR_VALID_PATTERN);
                  bmAttribute = new AtlasBusinessAttribute(attribute, entityTypes, maxStringLength, validPattern);
              } else {
                  bmAttribute = new AtlasBusinessAttribute(attribute, entityTypes);
              }
  
              a.put(attrName, bmAttribute);
          }
  
          super.allAttributes = Collections.unmodifiableMap(a);
      }
  
      @Override
      void resolveReferencesPhase2(AtlasTypeRegistry typeRegistry) throws AtlasBaseException {
          super.resolveReferencesPhase2(typeRegistry);
  
          for (AtlasAttribute attribute : super.allAttributes.values()) {
              AtlasBusinessAttribute bmAttribute = (AtlasBusinessAttribute) attribute;
              Set<AtlasEntityType>   entityTypes = bmAttribute.getApplicableEntityTypes();
  
              if (CollectionUtils.isNotEmpty(entityTypes)) {
                  for (AtlasEntityType entityType : entityTypes) {
                      entityType.addBusinessAttribute(bmAttribute);
                  }
              }
          }
      }
  
      public AtlasBusinessMetadataDef getBusinessMetadataDef() {
          return businessMetadataDef;
      }
  
      public static class AtlasBusinessAttribute extends AtlasAttribute {
          private final Set<AtlasEntityType> applicableEntityTypes;
          private final int                  maxStringLength;
          private final String               validPattern;
  
          public AtlasBusinessAttribute(AtlasAttribute attribute, Set<AtlasEntityType> applicableEntityTypes) {
              super(attribute);
  
              this.maxStringLength       = 0;
              this.validPattern          = null;
              this.applicableEntityTypes = applicableEntityTypes;
          }
  
          public AtlasBusinessAttribute(AtlasAttribute attribute, Set<AtlasEntityType> applicableEntityTypes, int maxStringLength, String validPattern) {
              super(attribute);
  
              this.maxStringLength       = maxStringLength;
              this.validPattern          = validPattern;
              this.applicableEntityTypes = applicableEntityTypes;
          }
  
          @Override
          public AtlasBusinessMetadataType getDefinedInType() {
              return (AtlasBusinessMetadataType) super.getDefinedInType();
          }
  
          public Set<AtlasEntityType> getApplicableEntityTypes() {
              return applicableEntityTypes;
          }
  
          public String getValidPattern() {
              return validPattern;
          }
  
          public int getMaxStringLength() {
              return maxStringLength;
          }
  
          public boolean isValidLength(Object value) {
              boolean ret = true;
              if (value != null) {
                  AtlasType attrType = getAttributeType();
  
                  if (attrType instanceof AtlasBuiltInTypes.AtlasStringType) {
                      ret = isValidStringValue(value);
                  } else if (attrType instanceof AtlasArrayType) {
                      attrType = ((AtlasArrayType) attrType).getElementType();
                      if (attrType instanceof AtlasBuiltInTypes.AtlasStringType) {
                          ret = isValidArrayValue(value);
                      }
                  }
              }
              return ret;
          }
  
          private boolean isValidStringValue(Object obj) {
              return obj == null || String.valueOf(obj).length() <= this.maxStringLength;
          }
  
          private boolean isValidArrayValue(Object obj) {
              if (obj instanceof List || obj instanceof Set) {
                  Collection objList = (Collection) obj;
  
                  for (Object element : objList) {
                      if (!isValidStringValue(element)) {
                          return false;
                      }
                  }
              } else if (obj.getClass().isArray()) {
                  int arrayLen = Array.getLength(obj);
                  for (int i = 0; i < arrayLen; i++) {
                      if (!isValidStringValue(Array.get(obj, i))) {
                          return false;
                      }
                  }
              }
              return true;
          }
      }
  }
  