net.firejack.platform.api.registry.broker.ReadBaseBIReportDataBroker.java Source code

Java tutorial

Introduction

Here is the source code for net.firejack.platform.api.registry.broker.ReadBaseBIReportDataBroker.java

Source

/*
 * 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
 *
 *   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 net.firejack.platform.api.registry.broker;

import net.firejack.platform.api.OPFEngine;
import net.firejack.platform.api.registry.domain.*;
import net.firejack.platform.api.registry.field.Field;
import net.firejack.platform.api.registry.model.BIReportLocation;
import net.firejack.platform.core.broker.ServiceBroker;
import net.firejack.platform.core.domain.NamedValues;
import net.firejack.platform.core.request.ServiceRequest;
import net.firejack.platform.core.response.ServiceResponse;
import net.firejack.platform.core.store.IAbstractStore;
import net.firejack.platform.core.utils.*;
import net.firejack.platform.utils.WebUtils;
import net.firejack.platform.utils.generate.FormattingUtils;
import org.hibernate.criterion.ProjectionList;
import org.hibernate.criterion.Projections;

import java.beans.PropertyDescriptor;
import java.util.*;

public class ReadBaseBIReportDataBroker
        extends ServiceBroker<ServiceRequest<NamedValues>, ServiceResponse<BIReportData>> {

    @Override
    protected ServiceResponse<BIReportData> perform(ServiceRequest<NamedValues> request) throws Exception {
        Long biReportUserId = (Long) request.getData().get("id");

        String parentNodeValues = (String) request.getData().get("parentNodeValues");
        String[] parentRowValues = WebUtils.deserializeJSON(parentNodeValues, String[].class);

        int depth = parentRowValues.length;

        ServiceResponse<BIReportData> response;

        ServiceResponse<BIReportUser> biReportUserResponse = OPFEngine.RegistryService
                .readBIReportUser(biReportUserId);
        if (biReportUserResponse.isSuccess()) {
            BIReportData biReport = new BIReportData();

            BIReportUser biReportUser = biReportUserResponse.getItem();
            List<BIReportUserField> biReportUserFields = biReportUser.getFields();

            Map<Long, BIReportField> entityBIReportFieldMap = new HashMap<Long, BIReportField>();
            for (BIReportField biReportField : biReportUser.getReport().getFields()) {
                if (biReportField.getField() == null) {
                    entityBIReportFieldMap.put(biReportField.getEntity().getId(), biReportField);
                }
            }

            Integer columnIndex = 0;
            List<BIReportColumn> biReportColumns = new ArrayList<BIReportColumn>();

            Map<BIReportField, BIReportLocation> groupedBIReportFields = new LinkedHashMap<BIReportField, BIReportLocation>();

            Map<Integer, BIReportRow> verticalColumnsMapping = new HashMap<Integer, BIReportRow>();
            List<BIReportUserField> verticalBIReportUserFields = getFieldsByLocation(biReportUserFields,
                    BIReportLocation.VERTICAL);
            Entity previousEntity = null;
            for (int i = 0; i < verticalBIReportUserFields.size(); i++) {
                BIReportUserField biReportUserField = verticalBIReportUserFields.get(i);
                BIReportField biReportField = biReportUserField.getField();
                Entity entity = biReportField.getEntity();

                if (previousEntity == null || !previousEntity.getId().equals(entity.getId())) {
                    String columnName = entity.getName();
                    BIReportField entityBIReportField = entityBIReportFieldMap.get(entity.getId());
                    if (entityBIReportField != null) {
                        columnName = StringUtils.defaultIfEmpty(entityBIReportField.getDisplayName(), columnName);
                    }

                    BIReportColumn biReportColumn = new BIReportColumn();
                    biReportColumn.setName(columnName);
                    biReportColumn.setType(BIReportLocation.VERTICAL);
                    biReportColumn.setColumnIndex(columnIndex);
                    biReportColumn.setExpanded(biReportUserField.isExpanded());
                    biReportColumn.setUnShift(i);
                    biReportColumns.add(biReportColumn);

                    columnIndex++;
                    previousEntity = entity;
                }

                BIReportRow biReportRow = new BIReportRow();
                biReportRow.setColumnIndex(biReportColumns.size() - 1);
                biReportRow.setExpanded(biReportUserField.isExpanded());
                verticalColumnsMapping.put(i, biReportRow);
                groupedBIReportFields.put(biReportField, BIReportLocation.VERTICAL);
            }
            biReport.setCountOfLevels(verticalColumnsMapping.size());

            List<BIReportUserField> horizontalBIReportUserFields = getFieldsByLocation(biReportUserFields,
                    BIReportLocation.HORIZONTAL);
            Map<Entity, List<BIReportField>> entityBIReportFields = new LinkedHashMap<Entity, List<BIReportField>>();
            for (BIReportUserField biReportUserField : horizontalBIReportUserFields) {
                BIReportField biReportField = biReportUserField.getField();
                Entity entity = biReportField.getEntity();

                List<BIReportField> biReportFields = entityBIReportFields.get(entity);
                if (biReportFields == null) {
                    biReportFields = new ArrayList<BIReportField>();
                    entityBIReportFields.put(entity, biReportFields);
                }
                biReportFields.add(biReportField);
                groupedBIReportFields.put(biReportField, BIReportLocation.HORIZONTAL);
            }

            List<BIReportUserField> measureBIReportUserFields = getFieldsByLocation(biReportUserFields,
                    BIReportLocation.MEASURE);
            for (BIReportUserField biReportUserField : measureBIReportUserFields) {
                BIReportField biReportField = biReportUserField.getField();
                groupedBIReportFields.put(biReportField, BIReportLocation.MEASURE);
            }

            String biReportUserFilter = biReportUser.getFilter();
            biReport.setFilter(biReportUserFilter);
            List<List<SearchQuery>> filterSearchQueries = WebUtils.deserializeJSON(biReportUserFilter, List.class,
                    List.class, SearchQuery.class);

            BIReportUserField measureBIReportUserField = measureBIReportUserFields.get(0);
            BIReportField measureBIReportField = measureBIReportUserField.getField();
            Entity factEntity = measureBIReportField.getEntity();
            String factDomainClassName = FormattingUtils.classFormatting(factEntity.getName());
            String factModelClassName = factEntity.getPath() + ".model." + factDomainClassName + "Model";

            Class<?> factModelClass = Class.forName(factModelClassName);

            List<Object[]> globalList = null;
            for (Map.Entry<Entity, List<BIReportField>> entry : entityBIReportFields.entrySet()) {
                Entity entity = entry.getKey();
                List<BIReportField> biReportFields = entry.getValue();

                String domainClassName = FormattingUtils.classFormatting(entity.getName());
                String modelClassName = entity.getPath() + ".model." + domainClassName + "Model";
                String storeClassName = entity.getPath() + ".store.Basic" + domainClassName + "Store";
                IAbstractStore store = OpenFlameSpringContext.getBean(storeClassName);

                List<List<SearchQuery>> columnSearchQueryList = new ArrayList<List<SearchQuery>>();
                List<SortField> sortFields = new ArrayList<SortField>();
                ProjectionList projectionList = Projections.projectionList();
                for (BIReportField biReportField : biReportFields) {
                    Field field = biReportField.getField();
                    String fieldName = FormattingUtils.fieldModelFormatting(field.getName());
                    projectionList.add(Projections.property(fieldName));
                    sortFields.add(new SortField(fieldName, SortOrder.ASC));

                    String fkDimFieldName = findDimFieldName(factModelClass, modelClassName);
                    for (List<SearchQuery> searchQueries : filterSearchQueries) {
                        List<SearchQuery> columnSearchQueries = new ArrayList<SearchQuery>();
                        for (SearchQuery searchQuery : searchQueries) {
                            String searchField = searchQuery.getField();
                            String[] fieldNames = searchField.split("\\.");
                            if (fieldNames.length == 2 && fieldNames[0].equals(fkDimFieldName)) {
                                SearchQuery columnSearchQuery = new SearchQuery(fieldNames[1],
                                        searchQuery.getOperation(), searchQuery.getValue());
                                columnSearchQueries.add(columnSearchQuery);
                            }
                        }
                        if (columnSearchQueries.isEmpty()) {
                            SearchQuery alwaysTrueSearchQuery = new SearchQuery();
                            columnSearchQueries.add(alwaysTrueSearchQuery);
                        }
                        columnSearchQueryList.add(columnSearchQueries);
                    }
                }

                Paging paging = new Paging(null, null, sortFields);
                List<Object[]> objectsList;
                if (biReportFields.size() > 1) {
                    objectsList = store.advancedSearchWithProjection(columnSearchQueryList,
                            Projections.distinct(projectionList), null, paging);
                } else {
                    objectsList = new ArrayList<Object[]>();
                    List<Object> objectList = store.advancedSearchWithProjection(columnSearchQueryList,
                            Projections.distinct(projectionList), null, paging);
                    for (Object object : objectList) {
                        objectsList.add(new Object[] { object });
                    }
                }
                if (globalList == null) {
                    globalList = objectsList;
                } else {
                    List<Object[]> mergedList = new ArrayList<Object[]>();
                    for (Object[] globalObjects : globalList) {
                        for (Object[] objects : objectsList) {
                            Object[] mergedObjects = ArrayUtils.addAll(globalObjects, objects);
                            mergedList.add(mergedObjects);
                        }
                    }
                    globalList = mergedList;
                }

            }

            List<BIReportField> measureBIReportFields = getFieldsByLocation(groupedBIReportFields,
                    BIReportLocation.MEASURE);
            Map<ArrayKey, Integer> horizontalColumnMapping = generateHorizontalColumns(globalList, biReportColumns,
                    measureBIReportFields, columnIndex);
            if (depth == 0) {
                biReport.setColumns(biReportColumns);
            }

            int countOfExpandedChildren = 1;
            int countOfVerticalColumns = verticalColumnsMapping.size();
            for (int i = depth; i < countOfVerticalColumns; i++) {
                BIReportRow verticalBIReportRow = verticalColumnsMapping.get(i);
                if (verticalBIReportRow.isExpanded()) {
                    countOfExpandedChildren++;
                } else {
                    break;
                }
            }

            ProjectionList projectionList = Projections.projectionList();
            List<SortField> sortFields = new ArrayList<SortField>();
            Map<String, String> aliases = new HashMap<String, String>();

            int positionOfLastExpandedColumns = depth + countOfExpandedChildren - 1;
            List<SearchQuery> childrenFilterSearchQueries = new ArrayList<SearchQuery>();
            List<BIReportField> verticalBIReportFields = getFieldsByLocation(groupedBIReportFields,
                    new BIReportLocation[] { BIReportLocation.VERTICAL });
            for (int i = 0; i < verticalBIReportFields.size(); i++) {
                BIReportField biReportField = verticalBIReportFields.get(i);
                Entity dimEntity = biReportField.getEntity();
                String dimDomainClassName = FormattingUtils.classFormatting(dimEntity.getName());
                String dimModelClassName = dimEntity.getPath() + ".model." + dimDomainClassName + "Model";

                String fkDimFieldName = findDimFieldName(factModelClass, dimModelClassName);
                aliases.put(fkDimFieldName, fkDimFieldName);

                Field field = biReportField.getField();
                String fieldName = FormattingUtils.fieldModelFormatting(field.getName());
                String aliasedFieldName = fkDimFieldName + "." + fieldName;
                projectionList.add(Projections.groupProperty(aliasedFieldName));

                sortFields.add(new SortField(aliasedFieldName, SortOrder.ASC));

                if (positionOfLastExpandedColumns >= i + 1) {
                    if (depth >= i + 1) {
                        SearchQuery searchQuery = new SearchQuery();
                        searchQuery.setField(aliasedFieldName);
                        searchQuery.setOperation(QueryOperation.EQUALS);
                        searchQuery.setValue(parentRowValues[i]);
                        childrenFilterSearchQueries.add(searchQuery);
                    }
                } else {
                    break;
                }
            }

            if (filterSearchQueries.isEmpty()) {
                filterSearchQueries.add(childrenFilterSearchQueries);
            } else {
                for (List<SearchQuery> searchQueries : filterSearchQueries) {
                    for (SearchQuery childrenFilterSearchQuery : childrenFilterSearchQueries) {
                        searchQueries.add(childrenFilterSearchQuery);
                    }
                }
            }

            List<BIReportField> horizontalBIReportFields = getFieldsByLocation(groupedBIReportFields,
                    new BIReportLocation[] { BIReportLocation.HORIZONTAL });
            for (BIReportField biReportField : horizontalBIReportFields) {
                Entity dimEntity = biReportField.getEntity();
                String dimDomainClassName = FormattingUtils.classFormatting(dimEntity.getName());
                String dimModelClassName = dimEntity.getPath() + ".model." + dimDomainClassName + "Model";

                String fkDimFieldName = findDimFieldName(factModelClass, dimModelClassName);
                aliases.put(fkDimFieldName, fkDimFieldName);

                Field field = biReportField.getField();
                String fieldName = FormattingUtils.fieldModelFormatting(field.getName());
                String aliasedFieldName = fkDimFieldName + "." + fieldName;
                projectionList.add(Projections.groupProperty(aliasedFieldName));

                sortFields.add(new SortField(aliasedFieldName, SortOrder.ASC));
            }

            for (BIReportField biReportField : measureBIReportFields) {
                Field field = biReportField.getField();
                String fieldName = FormattingUtils.fieldModelFormatting(field.getName());
                projectionList.add(Projections.count(fieldName));
            }

            String storeClassName = factEntity.getPath() + ".store.Basic" + factDomainClassName + "Store";
            IAbstractStore store = OpenFlameSpringContext.getBean(storeClassName);

            List<Object[]> dataList = store.advancedSearchWithProjection(filterSearchQueries,
                    Projections.distinct(projectionList), aliases, new Paging(null, null, sortFields));

            int countOfHorizontalColumns = horizontalBIReportUserFields.size();
            int countOfMeasureColumns = measureBIReportUserFields.size();

            List<BIReportRow> biReportRows = generateRows(dataList, verticalColumnsMapping, horizontalColumnMapping,
                    countOfVerticalColumns, countOfHorizontalColumns, countOfMeasureColumns, depth,
                    countOfExpandedChildren);
            biReport.setRows(biReportRows);

            response = new ServiceResponse<BIReportData>(biReport, "BI Report data", true);
        } else {
            response = new ServiceResponse<BIReportData>(
                    "Could not find user configuration of BIReport by ID: " + biReportUserId, false);
        }

        return response;
    }

    protected List<BIReportUserField> getFieldsByLocation(List<BIReportUserField> fields,
            BIReportLocation location) {
        List<BIReportUserField> locationFields = new ArrayList<BIReportUserField>();
        for (BIReportUserField biReportUserField : fields) {
            if (location.equals(biReportUserField.getLocation())) {
                locationFields.add(biReportUserField);
            }
        }
        Collections.sort(locationFields, new Comparator<BIReportUserField>() {
            @Override
            public int compare(BIReportUserField field1, BIReportUserField field2) {
                return field1.getOrder() - field2.getOrder();
            }
        });
        return locationFields;
    }

    protected List<BIReportField> getFieldsByLocation(Map<BIReportField, BIReportLocation> biReportFields,
            BIReportLocation[] locations) {
        List<BIReportField> locationFields = new ArrayList<BIReportField>();
        for (BIReportLocation location : locations) {
            locationFields.addAll(getFieldsByLocation(biReportFields, location));
        }
        return locationFields;
    }

    protected List<BIReportField> getFieldsByLocation(Map<BIReportField, BIReportLocation> biReportFields,
            BIReportLocation location) {
        List<BIReportField> locationFields = new ArrayList<BIReportField>();
        for (Map.Entry<BIReportField, BIReportLocation> entry : biReportFields.entrySet()) {
            BIReportLocation biReportLocation = entry.getValue();
            if (location.equals(biReportLocation)) {
                locationFields.add(entry.getKey());
            }
        }
        return locationFields;
    }

    protected Map<ArrayKey, Integer> generateHorizontalColumns(List<Object[]> list,
            List<BIReportColumn> biReportColumns, List<BIReportField> measureBIReportFields, Integer columnIndex) {
        Map<String, Map> mapColumns = new LinkedHashMap<String, Map>();
        for (Object[] objects : list) {
            buildMapColumns(objects, 0, mapColumns);
        }

        Map<ArrayKey, Integer> horizontalColumnMapping = new HashMap<ArrayKey, Integer>();
        generateHorizontalColumns(mapColumns, biReportColumns, measureBIReportFields, new Integer[] { columnIndex },
                new String[] {}, horizontalColumnMapping);
        return horizontalColumnMapping;
    }

    protected void buildMapColumns(Object[] objects, int i, Map<String, Map> mapColumns) {
        Object object = objects[i];
        String name = String.valueOf(object);
        Map<String, Map> childrenMapColumns = mapColumns.get(name);
        if (childrenMapColumns == null) {
            childrenMapColumns = new LinkedHashMap<String, Map>();
            mapColumns.put(name, childrenMapColumns);
        }
        if (objects.length > (i + 1)) {
            buildMapColumns(objects, ++i, childrenMapColumns);
        }
    }

    protected void generateHorizontalColumns(Map<String, Map> mapColumns, List<BIReportColumn> biReportColumns,
            List<BIReportField> measureBIReportFields, Integer[] columnIndex, String[] columnPaths,
            Map<ArrayKey, Integer> horizontalColumnMapping) {
        for (Map.Entry<String, Map> entry : mapColumns.entrySet()) {
            String name = entry.getKey();
            BIReportColumn biReportColumn = new BIReportColumn();
            biReportColumn.setName(name);
            biReportColumn.setType(BIReportLocation.HORIZONTAL);

            columnPaths = (String[]) ArrayUtils.add(columnPaths, name);

            List<BIReportColumn> childrenBIReportColumns = biReportColumn.getChildren();
            if (childrenBIReportColumns == null) {
                childrenBIReportColumns = new ArrayList<BIReportColumn>();
                biReportColumn.setChildren(childrenBIReportColumns);
            }
            Map<String, Map> childrenMapColumns = entry.getValue();
            if (!childrenMapColumns.isEmpty()) {
                generateHorizontalColumns(childrenMapColumns, childrenBIReportColumns, measureBIReportFields,
                        columnIndex, columnPaths, horizontalColumnMapping);
            } else {
                columnIndex = generateMeasureColumns(childrenBIReportColumns, measureBIReportFields, columnIndex,
                        columnPaths, horizontalColumnMapping);
            }

            columnPaths = (String[]) ArrayUtils.remove(columnPaths, columnPaths.length - 1);

            biReportColumns.add(biReportColumn);
        }
    }

    protected Integer[] generateMeasureColumns(List<BIReportColumn> childrenBIReportColumns,
            List<BIReportField> measureBIReportFields, Integer[] columnIndex, String[] columnPaths,
            Map<ArrayKey, Integer> horizontalColumnMapping) {
        for (int i = 0; i < measureBIReportFields.size(); i++) {
            BIReportField biReportField = measureBIReportFields.get(i);
            BIReportColumn biReportColumnMeasure = new BIReportColumn();
            biReportColumnMeasure.setName(biReportField.getDisplayName());
            biReportColumnMeasure.setType(BIReportLocation.MEASURE);
            biReportColumnMeasure.setColumnIndex(columnIndex[0]);
            childrenBIReportColumns.add(biReportColumnMeasure);

            columnPaths = (String[]) ArrayUtils.add(columnPaths, String.valueOf(i));
            horizontalColumnMapping.put(new ArrayKey((String[]) ArrayUtils.clone(columnPaths)), columnIndex[0]);
            columnPaths = (String[]) ArrayUtils.remove(columnPaths, columnPaths.length - 1);
            columnIndex[0]++;
        }
        return columnIndex;
    }

    protected List<BIReportRow> generateRows(List<Object[]> dataList,
            Map<Integer, BIReportRow> verticalColumnsMapping, Map<ArrayKey, Integer> horizontalColumnMapping,
            int countOfVerticalColumns, int countOfHorizontalColumns, int countOfMeasureColumns, int depth,
            int countExpandedColumns) {
        Map<String, BIReportRow> rootBIReportRowsMap = new LinkedHashMap<String, BIReportRow>();
        for (Object[] data : dataList) {
            int countOfVerticalDataColumns = depth + countExpandedColumns;
            int countOfAdditionalEmptyColumns = countOfVerticalColumns - countOfVerticalDataColumns;
            for (int i = 0; i < countOfAdditionalEmptyColumns; i++) {
                data = ArrayUtils.add(data, countOfVerticalDataColumns, "");
            }

            Map<String, BIReportRow> biReportRowsMap = rootBIReportRowsMap;
            for (int i = depth; i < countOfVerticalDataColumns; i++) {
                Object object = data[i];
                boolean isNullValue = object == null;
                String verticalRowValue = String.valueOf(object);
                BIReportRow biReportRow = biReportRowsMap.get(verticalRowValue);

                BIReportRow verticalBIReportRow = verticalColumnsMapping.get(i);
                Integer columnIndex = verticalBIReportRow.getColumnIndex();

                if (biReportRow == null) {
                    biReportRow = new BIReportRow();
                    biReportRow.setName(verticalRowValue);
                    biReportRow.setColumnIndex(columnIndex);
                    biReportRow.setExpanded(verticalBIReportRow.isExpanded());
                    biReportRow.setLeaf(countOfVerticalColumns == i + 1 || isNullValue);
                    biReportRow.setDepth(i + 1);
                    biReportRow.setBiReportRowMap(new LinkedHashMap<String, BIReportRow>());
                    biReportRowsMap.put(verticalRowValue, biReportRow);
                }

                if (countOfVerticalDataColumns == i + 1) {
                    Map<Integer, Double> columnIndexValues = biReportRow.getColumnIndexValues();
                    if (columnIndexValues == null) {
                        columnIndexValues = new HashMap<Integer, Double>();
                        biReportRow.setColumnIndexValues(columnIndexValues);
                    }
                    Object[] keyValues = ArrayUtils.subarray(data, countOfVerticalColumns,
                            countOfVerticalColumns + countOfHorizontalColumns);
                    for (int m = 0; m < countOfMeasureColumns; m++) {
                        keyValues = ArrayUtils.add(keyValues, String.valueOf(m));
                        columnIndex = horizontalColumnMapping.get(new ArrayKey(keyValues));
                        Double dataValue = Double.valueOf(String.valueOf(
                                ArrayUtils.subarray(data, countOfVerticalColumns + countOfHorizontalColumns + m,
                                        countOfVerticalColumns + countOfHorizontalColumns + m + 1)[0]));
                        Double cellValue = columnIndexValues.get(columnIndex);
                        if (cellValue == null) {
                            columnIndexValues.put(columnIndex, dataValue);
                        } else {
                            columnIndexValues.put(columnIndex, cellValue + dataValue);
                        }
                    }
                }

                biReportRowsMap = biReportRow.getBiReportRowMap();
            }
        }

        ArrayList<BIReportRow> biReportRows = new ArrayList<BIReportRow>(rootBIReportRowsMap.values());

        calculateRowTotals(null, biReportRows);

        return biReportRows;
    }

    protected void calculateRowTotals(BIReportRow parentBIReportRow, List<BIReportRow> biReportRows) {
        Map<Integer, Double> columnIndexTotalValues = new HashMap<Integer, Double>();
        for (BIReportRow biReportRow : biReportRows) {

            List<BIReportRow> childrenBIReportRow = biReportRow.getChildren();
            calculateRowTotals(biReportRow, childrenBIReportRow);

            Map<Integer, Double> columnIndexValues = biReportRow.getColumnIndexValues();
            for (Map.Entry<Integer, Double> entry : columnIndexValues.entrySet()) {
                Double columnIndexTotalValue = columnIndexTotalValues.get(entry.getKey());
                if (columnIndexTotalValue == null) {
                    columnIndexTotalValues.put(entry.getKey(), entry.getValue());
                } else {
                    columnIndexTotalValues.put(entry.getKey(), columnIndexTotalValue + entry.getValue());
                }
            }
            if (parentBIReportRow != null) {
                parentBIReportRow.setColumnIndexValues(columnIndexTotalValues);
            }
        }
    }

    protected String findDimFieldName(Class<?> factModelClass, String dimModelClassName) {
        String propertyName = null;
        PropertyDescriptor[] propertyDescriptors = ClassUtils.getPropertyDescriptors(factModelClass);
        for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
            if (propertyDescriptor.getReadMethod().getReturnType().getName().equals(dimModelClassName)) {
                propertyName = propertyDescriptor.getName();
                break;
            }
        }
        return propertyName;
    }

    protected static final class ArrayKey {

        private final String[] keys;

        public ArrayKey(Object[] objects) {
            this.keys = new String[objects.length];
            for (int i = 0; i < objects.length; i++) {
                this.keys[i] = String.valueOf(objects[i]);
            }
        }

        public ArrayKey(String[] keys) {
            this.keys = keys;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 23 * hash + Arrays.hashCode(this.keys);
            return hash;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj == null) {
                return false;
            }
            if (getClass() != obj.getClass()) {
                return false;
            }
            final ArrayKey other = (ArrayKey) obj;
            return Arrays.equals(this.keys, other.keys);
        }
    }

}