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.AtlasErrorCode;
  import org.apache.atlas.exception.AtlasBaseException;
  import org.apache.atlas.model.instance.AtlasClassification;
  import org.apache.atlas.model.instance.AtlasEntity;
  import org.apache.atlas.model.instance.AtlasObjectId;
  import org.apache.commons.lang.StringUtils;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Map;
  import java.util.Objects;
  
  public abstract class ImportTransformer {
      private static final String TRANSFORMER_PARAMETER_SEPARATOR = "~";
  
      private static final String TRANSFORMER_NAME_ADD                   = "add";
      private static final String TRANSFORMER_NAME_CLEAR_ATTR            = "clearAttrValue";
      private static final String TRANSFORMER_NAME_LOWERCASE             = "lowercase";
      private static final String TRANSFORMER_NAME_UPPERCASE             = "uppercase";
      private static final String TRANSFORMER_NAME_REMOVE_CLASSIFICATION = "removeClassification";
      private static final String TRANSFORMER_NAME_ADD_CLASSIFICATION    = "addClassification";
      private static final String TRANSFORMER_NAME_REPLACE               = "replace";
      private static final String TRANSFORMER_SET_DELETED                = "setDeleted";
  
      private final String transformType;
  
      protected ImportTransformer(String transformType) {
          this.transformType = transformType;
      }
  
      public static ImportTransformer getTransformer(String transformerSpec) throws AtlasBaseException {
          if (transformerSpec.contains(":") && !transformerSpec.contains(TRANSFORMER_PARAMETER_SEPARATOR)) {
              throw new AtlasBaseException(AtlasErrorCode.INVALID_VALUE,
                      "Invalid transformer specification. The parameter separator ':' is no longer supported. Use '~' instead. Transformer specification: " + transformerSpec);
          }
  
          String[] params = StringUtils.split(transformerSpec, TRANSFORMER_PARAMETER_SEPARATOR);
          String   key    = (params == null || params.length < 1) ? transformerSpec : params[0];
  
          final ImportTransformer ret;
  
          if (StringUtils.isEmpty(key)) {
              throw new AtlasBaseException(AtlasErrorCode.INVALID_VALUE, "Error creating ImportTransformer. Invalid transformer-specification: {}.", transformerSpec);
          } else if (key.equals(TRANSFORMER_NAME_REPLACE)) {
              String toFindStr  = (params == null || params.length < 2) ? "" : params[1];
              String replaceStr = (params == null || params.length < 3) ? "" : params[2];
  
              ret = new Replace(toFindStr, replaceStr);
          } else if (key.equals(TRANSFORMER_NAME_LOWERCASE)) {
              ret = new Lowercase();
          } else if (key.equals(TRANSFORMER_NAME_UPPERCASE)) {
              ret = new Uppercase();
          } else if (key.equals(TRANSFORMER_NAME_REMOVE_CLASSIFICATION)) {
              String name = (params == null || params.length < 1) ? "" : StringUtils.join(params, "~", 1, params.length);
              ret = new RemoveClassification(name);
          } else if (key.equals(TRANSFORMER_NAME_ADD)) {
              String name = (params == null || params.length < 1) ? "" : StringUtils.join(params, "~", 1, params.length);
              ret = new AddValueToAttribute(name);
          } else if (key.equals(TRANSFORMER_NAME_CLEAR_ATTR)) {
              String name = (params == null || params.length < 1) ? "" : StringUtils.join(params, "~", 1, params.length);
              ret = new ClearAttributes(name);
          } else if (key.equals(TRANSFORMER_SET_DELETED)) {
              ret = new SetDeleted();
          } else if (key.equals(TRANSFORMER_NAME_ADD_CLASSIFICATION)) {
              String name = (params == null || params.length < 1) ? "" : params[1];
              ret = new AddClassification(name, (params != null && params.length == 3) ? params[2] : "");
          } else {
              throw new AtlasBaseException(AtlasErrorCode.INVALID_VALUE, "Error creating ImportTransformer. Unknown transformer: {}.", transformerSpec);
          }
  
          return ret;
      }
  
      public String getTransformType() {
          return transformType;
      }
  
      public abstract Object apply(Object o) throws AtlasBaseException;
  
      static class Replace extends ImportTransformer {
          private final String toFindStr;
          private final String replaceStr;
  
          public Replace(String toFindStr, String replaceStr) {
              super(TRANSFORMER_NAME_REPLACE);
  
              this.toFindStr  = toFindStr;
              this.replaceStr = replaceStr;
          }
  
          public String getToFindStr() {
              return toFindStr;
          }
  
          public String getReplaceStr() {
              return replaceStr;
          }
  
          @Override
          public Object apply(Object o) {
              Object ret = o;
  
              if (o instanceof String) {
                  ret = StringUtils.replace((String) o, toFindStr, replaceStr);
              }
  
              return ret;
          }
      }
  
      static class Lowercase extends ImportTransformer {
          public Lowercase() {
              super(TRANSFORMER_NAME_LOWERCASE);
          }
  
          @Override
          public Object apply(Object o) {
              Object ret = o;
  
              if (o instanceof String) {
                  ret = StringUtils.lowerCase((String) o);
              }
  
              return ret;
          }
      }
  
      static class Uppercase extends ImportTransformer {
          public Uppercase() {
              super(TRANSFORMER_NAME_UPPERCASE);
          }
  
          @Override
          public Object apply(Object o) {
              Object ret = o;
  
              if (o instanceof String) {
                  ret = StringUtils.upperCase((String) o);
              }
  
              return ret;
          }
      }
  
      static class AddClassification extends ImportTransformer {
          private static final String FILTER_SCOPE_TOP_LEVEL = "topLevel";
  
          private final String              scope;
          private final String              classificationName;
          private final List<AtlasObjectId> filters;
  
          public AddClassification(String name, String scope) {
              super(TRANSFORMER_NAME_REMOVE_CLASSIFICATION);
  
              this.classificationName = name;
              this.scope              = scope;
              filters                 = new ArrayList<>();
          }
  
          public void addFilter(AtlasObjectId objectId) {
              filters.add(objectId);
          }
  
          @Override
          public Object apply(Object o) {
              if (!(o instanceof AtlasEntity)) {
                  return o;
              }
  
              AtlasEntity entity = (AtlasEntity) o;
              if (!passThruFilters(entity)) {
                  return o;
              }
  
              if (entity.getClassifications() == null) {
                  entity.setClassifications(new ArrayList<>());
              }
  
              for (AtlasClassification c : entity.getClassifications()) {
                  if (c.getTypeName().equals(classificationName)) {
                      return entity;
                  }
              }
  
              entity.getClassifications().add(new AtlasClassification(classificationName));
  
              return entity;
          }
  
          @Override
          public String toString() {
              return String.format("%s=%s", "AddClassification", classificationName);
          }
  
          public String getClassificationName() {
              return classificationName;
          }
  
          private boolean passThruFilters(AtlasEntity entity) {
              if (StringUtils.isEmpty(scope) || !scope.equals(FILTER_SCOPE_TOP_LEVEL)) {
                  return true;
              }
  
              for (AtlasObjectId filter : filters) {
                  if (isMatch(filter, entity)) {
                      return true;
                  }
              }
  
              return false;
          }
  
          private boolean isMatch(AtlasObjectId objectId, AtlasEntity entity) {
              boolean ret;
  
              if (StringUtils.isEmpty(objectId.getGuid())) {
                  ret = Objects.equals(objectId.getTypeName(), entity.getTypeName());
  
                  if (ret) {
                      for (Map.Entry<String, Object> entry : objectId.getUniqueAttributes().entrySet()) {
                          ret = ret && Objects.equals(entity.getAttribute(entry.getKey()), entry.getValue());
  
                          if (!ret) {
                              break;
                          }
                      }
                  }
  
                  return ret;
              } else {
                  return Objects.equals(objectId.getGuid(), entity.getGuid());
              }
          }
      }
  
      static class RemoveClassification extends ImportTransformer {
          private final String classificationToBeRemoved;
  
          public RemoveClassification(String name) {
              super(TRANSFORMER_NAME_REMOVE_CLASSIFICATION);
  
              this.classificationToBeRemoved = name;
          }
  
          @Override
          public Object apply(Object o) {
              if (!(o instanceof AtlasEntity)) {
                  return o;
              }
  
              AtlasEntity entity = (AtlasEntity) o;
  
              if (entity.getClassifications().isEmpty()) {
                  return o;
              }
  
              List<AtlasClassification> toRemove = null;
  
              for (AtlasClassification classification : entity.getClassifications()) {
                  if (classification.getTypeName().equals(classificationToBeRemoved)) {
                      if (toRemove == null) {
                          toRemove = new ArrayList<>();
                      }
  
                      toRemove.add(classification);
                  }
              }
  
              if (toRemove != null) {
                  entity.getClassifications().removeAll(toRemove);
              }
  
              return entity;
          }
  
          @Override
          public String toString() {
              return String.format("%s=%s", "RemoveClassification", classificationToBeRemoved);
          }
      }
  
      static class AddValueToAttribute extends ImportTransformer {
          private final String nameValuePair;
          private       String attrName;
          private       String attrValueRaw;
          private       Object attrValue;
  
          protected AddValueToAttribute(String nameValuePair) {
              super(TRANSFORMER_NAME_ADD);
  
              this.nameValuePair = nameValuePair;
  
              setAttrNameValue(this.nameValuePair);
          }
  
          @Override
          public Object apply(Object o) {
              if (o == null) {
                  return o;
              }
  
              if (!(o instanceof AtlasEntity)) {
                  return o;
              }
  
              AtlasEntity entity            = (AtlasEntity) o;
              Object      attrExistingValue = entity.getAttribute(attrName);
  
              if (attrExistingValue == null) {
                  entity.setAttribute(attrName, attrValue);
              } else if (attrExistingValue instanceof List) {
                  List list = (List<?>) attrExistingValue;
  
                  if (attrValue instanceof List) {
                      list.addAll((List<?>) attrValue);
                  } else {
                      list.add(attrValue);
                  }
              } else {
                  entity.setAttribute(attrName, attrValueRaw);
              }
  
              return entity;
          }
  
          private void setAttrNameValue(String nameValuePair) {
              String separatorEquals = "=";
  
              if (!nameValuePair.contains(separatorEquals)) {
                  return;
              }
  
              String[] splits = StringUtils.split(nameValuePair, separatorEquals);
  
              if (splits.length == 0) {
                  return;
              }
  
              attrName = splits[0];
  
              if (splits.length >= 2) {
                  attrValueRaw = splits[1];
              }
  
              setAttrValue(attrValueRaw);
          }
  
          private void setAttrValue(String attrValueRaw) {
              final String typePrefix = "list:";
  
              if (attrValueRaw.startsWith(typePrefix)) {
                  final String item = StringUtils.remove(attrValueRaw, typePrefix);
  
                  attrValue = new ArrayList<>(Collections.singletonList(item));
              } else {
                  attrValue = attrValueRaw;
              }
          }
      }
  
      static class ClearAttributes extends ImportTransformer {
          private final String[] attrNames;
  
          protected ClearAttributes(String attrNames) {
              super(TRANSFORMER_NAME_CLEAR_ATTR);
  
              this.attrNames = StringUtils.split(attrNames, ",");
          }
  
          @Override
          public Object apply(Object o) {
              if (o == null) {
                  return o;
              }
  
              if (!(o instanceof AtlasEntity)) {
                  return o;
              }
  
              AtlasEntity entity = (AtlasEntity) o;
  
              for (String attrName : attrNames) {
                  entity.setAttribute(attrName, null);
              }
  
              return entity;
          }
      }
  
      static class SetDeleted extends ImportTransformer {
          protected SetDeleted() {
              super(TRANSFORMER_SET_DELETED);
          }
  
          @Override
          public Object apply(Object o) {
              if (o == null) {
                  return o;
              }
  
              if (!(o instanceof AtlasEntity)) {
                  return o;
              }
  
              AtlasEntity entity = (AtlasEntity) o;
  
              entity.setStatus(AtlasEntity.Status.DELETED);
  
              return entity;
          }
      }
  }
  