/* * DBeaver - Universal Database Manager * Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org) * * Licensed 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 org.jkiss.dbeaver.model.impl.data.transformers; import org.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.data.*; import org.jkiss.dbeaver.model.exec.DBCSession; import org.jkiss.dbeaver.model.struct.DBSAttributeBase; import org.jkiss.utils.Pair; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; /** * Transforms attribute of map type into hierarchy of attributes */ public class MapAttributeTransformer implements DBDAttributeTransformer { @Override public void transformAttribute(@NotNull DBCSession session, @NotNull DBDAttributeBinding attribute, @NotNull List<Object[]> rows, @NotNull Map<String, String> options) throws DBException { resolveMapsFromData(session, attribute, rows); } static void resolveMapsFromData(DBCSession session, DBDAttributeBinding attribute, List<Object[]> rows) throws DBException { // Analyse rows and extract meta information from values List<Pair<DBSAttributeBase, Object[]>> valueAttributes = null; for (int i = 0; i < rows.size(); i++) { Object value = rows.get(i)[attribute.getOrdinalPosition()]; if (value instanceof DBDCollection) { // Use first element to discover structure DBDCollection collection = (DBDCollection) value; if (collection.getItemCount() > 0) { value = collection.getItem(0); } } if (value instanceof DBDComposite) { DBSAttributeBase[] attributes = ((DBDComposite) value).getAttributes(); for (DBSAttributeBase attr : attributes) { Pair<DBSAttributeBase, Object[]> attrValue = null; if (valueAttributes != null) { for (Pair<DBSAttributeBase, Object[]> pair : valueAttributes) { if (pair.getFirst().getName().equals(attr.getName())) { attrValue = pair; break; } } } if (attrValue != null) { // Update attr value attrValue.getSecond()[i] = ((DBDComposite) value).getAttributeValue(attr); } else { Object[] valueList = new Object[rows.size()]; valueList[i] = ((DBDComposite) value).getAttributeValue(attr); if (valueAttributes == null) { valueAttributes = new ArrayList<>(); } valueAttributes.add( new Pair<>( attr, valueList)); } } } } if (valueAttributes != null && !valueAttributes.isEmpty()) { createNestedMapBindings(session, attribute, valueAttributes); } } private static void createNestedMapBindings(DBCSession session, DBDAttributeBinding topAttribute, List<Pair<DBSAttributeBase, Object[]>> nestedAttributes) throws DBException { int maxPosition = 0; for (Pair<DBSAttributeBase, Object[]> attr : nestedAttributes) { maxPosition = Math.max(maxPosition, attr.getFirst().getOrdinalPosition()); } List<DBDAttributeBinding> nestedBindings = topAttribute.getNestedBindings(); if (nestedBindings == null) { nestedBindings = new ArrayList<>(); } else { for (DBDAttributeBinding binding : nestedBindings) { maxPosition = Math.max(maxPosition, binding.getOrdinalPosition()); } } Object[] fakeRow = new Object[maxPosition + 1]; List<Object[]> fakeRows = Collections.singletonList(fakeRow); for (Pair<DBSAttributeBase, Object[]> nestedAttr : nestedAttributes) { DBSAttributeBase attribute = nestedAttr.getFirst(); Object[] values = nestedAttr.getSecond(); DBDAttributeBinding nestedBinding = null; for (DBDAttributeBinding binding : nestedBindings) { if (binding.getName().equals(attribute.getName())) { nestedBinding = binding; break; } } if (nestedBinding == null) { nestedBinding = new DBDAttributeBindingType(topAttribute, attribute); nestedBindings.add(nestedBinding); } if (attribute.getDataKind().isComplex()) { // Make late binding for each row value for (int i = 0; i < values.length; i++) { if (DBUtils.isNullValue(values[i])) { continue; } fakeRow[nestedBinding.getOrdinalPosition()] = values[i]; nestedBinding.lateBinding(session, fakeRows); } } } if (!nestedBindings.isEmpty()) { topAttribute.setNestedBindings(nestedBindings); } } }