/******************************************************************************* * * Copyright 2012-2015, the original author or authors. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obta a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * 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 com.flipkart.aesop.mapper.utils; import com.flipkart.aesop.event.AbstractEvent; import com.flipkart.aesop.mapper.enums.MapAllValues; import com.flipkart.aesop.mapper.implementation.DefaultMapperImpl; import com.flipkart.aesop.mapper.implementation.MapperType; import com.typesafe.config.ConfigObject; import com.typesafe.config.ConfigValue; import java.util.*; import java.util.Map.Entry; /** * Provides static functions to be used in Mapper Logic in {@link DefaultMapperImpl}, {@link MapperType}. * @author Prakhar Jain */ public class MapperHelper { /** * Gets Namespace mapAll Path. * @param configRoot * @return Namespace mapAll Path */ public static String getNamespaceMapAllPath(String configRoot) { String namespaceMapAllPath = configRoot + MapperConstants.CONFIG_PATH_SEPARATOR + MapperConstants.MAP_ALL_FIELD_NAME; return namespaceMapAllPath; } /** * Gets Column mapAll Path. * @param configRoot * @param sourceEvent * @return Column mapAll Path */ public static String getColumnMapAllPath(String configRoot, AbstractEvent sourceEvent) { String columnMapAllPath = getEntityPath(configRoot, sourceEvent) + MapperConstants.CONFIG_PATH_SEPARATOR + MapperConstants.MAP_ALL_FIELD_NAME; return columnMapAllPath; } /** * Gets Column exclusionList Path. * @param configRoot * @param sourceEvent * @return Column exclusionList Path */ public static String getColumnExclusionListPath(String configRoot, AbstractEvent sourceEvent) { String columnExclusionListPath = getEntityPath(configRoot, sourceEvent) + MapperConstants.CONFIG_PATH_SEPARATOR + MapperConstants.EXCLUSION_LIST_FIELD_NAME; return columnExclusionListPath; } /** * Gets Entity mapAll Path. * @param configRoot * @param event * @return Entity mapAll Path */ public static String getEntityMapAllPath(String configRoot, AbstractEvent event) { String entityMapAllPath = getNamespacePath(configRoot, event) + MapperConstants.CONFIG_PATH_SEPARATOR + MapperConstants.MAP_ALL_FIELD_NAME; return entityMapAllPath; } /** * Gets Entity exclusionList Path. * @param configRoot * @param event * @return Entity exclusionList Path */ public static String getEntityExclusionListPath(String configRoot, AbstractEvent event) { String entityExclusionListPath = getNamespacePath(configRoot, event) + MapperConstants.CONFIG_PATH_SEPARATOR + MapperConstants.EXCLUSION_LIST_FIELD_NAME; return entityExclusionListPath; } /** * Gets Namespace exclusionList Path. * @param configRoot * @return Namespace exclusionList Path */ public static String getNamespaceExclusionListPath(String configRoot) { String namespaceExclusionListPath = configRoot + MapperConstants.CONFIG_PATH_SEPARATOR + MapperConstants.EXCLUSION_LIST_FIELD_NAME; return namespaceExclusionListPath; } /** * Gets Namespace Path. * @param configRoot * @param event * @return Namespace Path */ public static String getNamespacePath(String configRoot, AbstractEvent event) { String namespacePath = configRoot + MapperConstants.CONFIG_PATH_SEPARATOR + event.getNamespaceName(); return namespacePath; } /** * Gets Entity Path. * @param configRoot * @param event * @return Entity Path. */ public static String getEntityPath(String configRoot, AbstractEvent event) { String entityPath = getNamespacePath(configRoot, event) + MapperConstants.CONFIG_PATH_SEPARATOR + event.getEntityName(); return entityPath; } /** * Gets Entity groupNo Path. * @param configRoot * @param event * @return Entity groupNo Path */ public static String getEntityGroupNoPath(String configRoot, AbstractEvent event) { String entityPath = getEntityPath(configRoot, event) + MapperConstants.CONFIG_PATH_SEPARATOR + MapperConstants.DESTINATION_ENTITY_GROUP_NO_FIELD_NAME; return entityPath; } /** * Gets columnMap Path. * @param configRoot * @param event * @return columnMap Path */ public static String getColumnMapPath(String configRoot, AbstractEvent event) { String entityPath = getEntityPath(configRoot, event) + MapperConstants.CONFIG_PATH_SEPARATOR + MapperConstants.DESTINATION_COLUMN_MAPPING; return entityPath; } /** * Gets primaryKeyList Path. * @param configRoot * @param event * @return primaryKeyList Path */ public static String getPrimaryKeyListPath(String configRoot, AbstractEvent event) { String entityPath = getEntityPath(configRoot, event) + MapperConstants.CONFIG_PATH_SEPARATOR + MapperConstants.DESTINATION_PRIMARY_KEY_LIST; return entityPath; } /** * Generates the Destination Event Column Mapping using source column mapping, and, exclusionList and mapAll * specified in the HOCON-config. * @param sourceEventColumnMap * @param columnMappingConfigObject * @param mapAll * @param exclusionSet * @return Destination Event Column Mapping */ @SuppressWarnings("unchecked") public static Map<String, Object> getDestinationEventColumnMapping(Map<String, Object> sourceEventColumnMap, ConfigObject columnMappingConfigObject, MapAllValues mapAll, Set<String> exclusionSet) { Map<String, Object> destinationColumnMap = new HashMap<String, Object>(); for (Entry<String, Object> sourceColumnEntry : sourceEventColumnMap.entrySet()) { String sourceColumnName = sourceColumnEntry.getKey(); Object sourceColumnValue = sourceColumnEntry.getValue(); if (columnMappingConfigObject != null && columnMappingConfigObject.containsKey(sourceColumnName)) { ConfigValue sourceColumnConfig = columnMappingConfigObject.get(sourceColumnName); String destinationColumn = sourceColumnConfig.unwrapped().toString(); /*Check if Destination Column is of type Column...NestedField*/ if (destinationColumn.contains(".")) { String destinationNestedField[] = destinationColumn.split("\\."); /*destinationNestedField[0] has Column, destinationNestedField[1] has NestedField1, destinationNestedField[2] has NestedField2 ... */ Map<String,Object> destinationFieldMap = destinationColumnMap; /* has reference to higherup field map*/ Map<String,Object> nestedFieldMap; /* has reference to nested field map */ for(int i=0; i<destinationNestedField.length-1;i++) { /*get current field if already exists, else create new field*/ nestedFieldMap = (destinationFieldMap.containsKey(destinationNestedField[i]) ? (Map<String,Object>)destinationFieldMap.get(destinationNestedField[i]) : new HashMap<String,Object>()); if(nestedFieldMap.isEmpty()) { /*insert in higherup field if empty*/ destinationFieldMap.put(destinationNestedField[i],nestedFieldMap); } destinationFieldMap=nestedFieldMap; /*point higherup field map to current nestedFieldMap*/ if(i==destinationNestedField.length-2) { /*if 1 level up then lowest level, put the lowest level in current fieldmap*/ nestedFieldMap.put(destinationNestedField[destinationNestedField.length-1],sourceColumnValue); } } } else { destinationColumnMap.put(destinationColumn, sourceColumnValue); } } else if (mapAll == MapAllValues.TRUE && !exclusionSet.contains(sourceColumnName)) { destinationColumnMap.put(sourceColumnName, sourceColumnValue); } } return destinationColumnMap; } /** * Generates destination event column mapping using <code>columnMappingConfig</code>, <code>exclusionSet</code> and <code>mapAll</code>.<br> * Allows mapping source fields to any r-th level destination list field. * @param sourceEventColumnMap k-v map of source event attributes * @param columnMappingConfigObject HOCON config for mapping src event to destination event. * @param mapAll flag whether as-is src to dest event field mapping should be used * @param exclusionSet set of src fields which needs to be excluded if <code>mapAll</code> is set to MapAllValues.TRUE * @return destination event field mapping */ @SuppressWarnings("unchecked") public static Map<String, Object> getEventColumnMapping(Map<String, Object> sourceEventColumnMap, ConfigObject columnMappingConfigObject, MapAllValues mapAll, Set<String> exclusionSet) { Map<String, Object> destinationColumnMap = new HashMap<String, Object>(); for(String sourceColumn: sourceEventColumnMap.keySet()) { if(null != columnMappingConfigObject && columnMappingConfigObject.containsKey(sourceColumn)) { String sourceColumnConfig = columnMappingConfigObject.get(sourceColumn).unwrapped().toString(); Object sourceColumnValue = sourceEventColumnMap.get(sourceColumn); //Check if destination column type is nested if(sourceColumnConfig.contains(".")) { String destinationNestedFields[] = sourceColumnConfig.split("\\."); List<Object> nestedFieldList = null; Map<String, Object> nestedFieldMap = destinationColumnMap; boolean currLevelMap = true; for(int w = 0; w < destinationNestedFields.length ; ++w) { final String wLevelField = destinationNestedFields[w]; //Check if destination current w-th level field is a list mapping if(wLevelField.endsWith("[]")) { String fieldName = wLevelField.replace("[]", ""); //Check if previous level under which current w-th level field is //contained is a map if(currLevelMap) { List<Object> wthLevelFieldList = nestedFieldMap.containsKey(fieldName) ? (ArrayList<Object>)nestedFieldMap.get(fieldName) : new ArrayList<Object>(); if(destinationNestedFields.length-1 == w) wthLevelFieldList.add(sourceColumnValue); nestedFieldMap.put(fieldName, wthLevelFieldList); nestedFieldList = wthLevelFieldList; //Alternatively previous level under which current w-th level field is //contained is a list } else { Map<String, Object> wthLevelFieldMap = nestedFieldList.iterator().hasNext() ? (HashMap<String, Object>)nestedFieldList.iterator().next() : new HashMap<String, Object>(); nestedFieldList.clear(); nestedFieldList.add(wthLevelFieldMap); List<Object> innerFieldSet = wthLevelFieldMap.containsKey(fieldName) ? (ArrayList<Object>)wthLevelFieldMap.get(fieldName) : new ArrayList<Object>(); if(destinationNestedFields.length-1 == w) innerFieldSet.add(sourceColumnValue); wthLevelFieldMap.put(fieldName, innerFieldSet); nestedFieldList = innerFieldSet; } currLevelMap = false; //If otherwise the current destination w-th level field is a non list-mapping } else { //Check if previous level under which current w-th level field is //contained is a map if(currLevelMap) { if(destinationNestedFields.length-1 == w) { nestedFieldMap.put(wLevelField, sourceColumnValue); break; } Map<String, Object> wthLevelFieldMap = nestedFieldMap.containsKey(wLevelField) ? (HashMap<String, Object>)nestedFieldMap.get(wLevelField) : new HashMap<String, Object>(); nestedFieldMap.put(wLevelField, wthLevelFieldMap); nestedFieldMap = wthLevelFieldMap; //Alternatively previous level under which current w-th level field is //contained is a list } else { HashMap<String, Object> wthLevelFieldMap = (nestedFieldList.iterator().hasNext()) ? (HashMap<String, Object>) nestedFieldList.iterator().next() : new HashMap<String, Object>(); nestedFieldList.clear(); nestedFieldList.add(wthLevelFieldMap); if(destinationNestedFields.length-1 == w) { wthLevelFieldMap.put(wLevelField, sourceColumnValue); break; } HashMap<String, Object> innerFieldMap = (wthLevelFieldMap.containsKey(wLevelField)) ? (HashMap<String, Object>) wthLevelFieldMap.get(wLevelField) : new HashMap<String, Object>(); wthLevelFieldMap.put(wLevelField, innerFieldMap); nestedFieldMap = innerFieldMap; } currLevelMap = true; } } //Check if destination column type is non-nested } else { //destination column is a list mapping if(sourceColumnConfig.endsWith("[]")) { String columnName = sourceColumnConfig.replace("[]", ""); List<Object> fieldValues = (destinationColumnMap.containsKey(columnName)) ? (ArrayList<Object>) destinationColumnMap.get(columnName) : new ArrayList<Object>(); fieldValues.add(sourceColumnValue); destinationColumnMap.put(columnName, fieldValues); //destination column is a map } else { destinationColumnMap.put(sourceColumnConfig, sourceColumnValue); } } } else if (MapAllValues.TRUE == mapAll && !exclusionSet.contains(sourceColumn)){ destinationColumnMap.put(sourceColumn, sourceEventColumnMap.get(sourceColumn)); } } return destinationColumnMap; } }