/*
* 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.data;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.*;
import org.jkiss.dbeaver.model.exec.DBCAttributeMetaData;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.exec.DBCSession;
import org.jkiss.dbeaver.model.sql.SQLConstants;
import org.jkiss.dbeaver.model.sql.SQLDataSource;
import org.jkiss.dbeaver.model.sql.SQLUtils;
import org.jkiss.dbeaver.model.struct.*;
import org.jkiss.dbeaver.model.virtual.DBVUtils;
import java.util.List;
import java.util.Map;
/**
* Base attribute binding
*/
public abstract class DBDAttributeBinding implements DBSObject, DBSAttributeBase, DBSTypedObjectEx, DBPQualifiedObject {
@NotNull
protected DBDValueHandler valueHandler;
@Nullable
protected DBSAttributeBase presentationAttribute;
@Nullable
private List<DBDAttributeBinding> nestedBindings;
private boolean transformed;
protected DBDAttributeBinding(@NotNull DBDValueHandler valueHandler)
{
this.valueHandler = valueHandler;
}
@Nullable
@Override
public abstract DBDAttributeBinding getParentObject();
/**
* Attribute index in result set
* @return attribute index (zero based)
*/
@Override
public abstract int getOrdinalPosition();
/**
* Attribute label
*/
@NotNull
public abstract String getLabel();
/**
* Attribute name
*/
@NotNull
public abstract String getName();
/**
* Meta attribute (obtained from result set)
*/
@NotNull
public abstract DBCAttributeMetaData getMetaAttribute();
/**
* Entity attribute (may be null).
* It is always null if {@link #lateBinding(DBCSession, List)} wasn't called
*/
@Nullable
public abstract DBSEntityAttribute getEntityAttribute();
/**
* Most valuable attribute reference.
* @return resolved entity attribute or just meta attribute
*/
@NotNull
public DBSAttributeBase getAttribute()
{
DBSEntityAttribute attr = getEntityAttribute();
return attr == null ? getMetaAttribute() : attr;
}
/**
* Presentation attribute.
* Usually the same as {@link #getAttribute()} but may be explicitly set by attribute transformers.
*/
@NotNull
public DBSAttributeBase getPresentationAttribute() {
if (presentationAttribute != null) {
return presentationAttribute;
}
return getAttribute();
}
public void setPresentationAttribute(@Nullable DBSAttributeBase presentationAttribute) {
this.presentationAttribute = presentationAttribute;
}
public boolean isPseudoAttribute() {
return false;
}
/**
* Row identifier (may be null)
*/
@Nullable
public abstract DBDRowIdentifier getRowIdentifier();
@Nullable
public abstract List<DBSEntityReferrer> getReferrers();
@Nullable
public abstract Object extractNestedValue(@NotNull Object ownerValue)
throws DBCException;
/**
* Attribute value handler
*/
@NotNull
public DBDValueHandler getValueHandler() {
return valueHandler;
}
public void setTransformHandler(@NotNull DBDValueHandler valueHandler) {
this.valueHandler = valueHandler;
this.transformed = true;
}
public boolean isTransformed() {
return transformed;
}
@NotNull
public DBDValueRenderer getValueRenderer() {
return valueHandler;
}
public boolean matches(@Nullable DBSAttributeBase attr, boolean searchByName) {
if (attr != null && (this == attr || getMetaAttribute() == attr || getEntityAttribute() == attr)) {
return true;
}
if (searchByName) {
if (attr instanceof DBDAttributeBinding) {
DBDAttributeBinding cmpAttr = (DBDAttributeBinding) attr;
if (getLevel() != cmpAttr.getLevel() || getOrdinalPosition() != cmpAttr.getOrdinalPosition()) {
return false;
}
// Match all hierarchy names
for (DBDAttributeBinding a1 = cmpAttr, a2 = this; a1 != null && a2 != null; a1 = a1.getParentObject(), a2 = a2.getParentObject()) {
if (!SQLUtils.compareAliases(attr.getName(), this.getName())) {
return false;
}
}
return true;
} else if (attr != null) {
return SQLUtils.compareAliases(attr.getName(), this.getName());
}
}
return false;
}
@Nullable
public List<DBDAttributeBinding> getNestedBindings() {
return nestedBindings;
}
public boolean hasNestedBindings() {
return nestedBindings != null;
}
public void setNestedBindings(@NotNull List<DBDAttributeBinding> nestedBindings) {
this.nestedBindings = nestedBindings;
}
@Nullable
@Override
public String getDescription() {
DBSEntityAttribute attr = getEntityAttribute();
return attr == null ? null : attr.getDescription();
}
@NotNull
@Override
public String getFullyQualifiedName(DBPEvaluationContext context) {
final DBPDataSource dataSource = getDataSource();
if (getParentObject() == null) {
return DBUtils.getQuotedIdentifier(dataSource, getName());
}
char structSeparator = SQLConstants.STRUCT_SEPARATOR;
if (dataSource instanceof SQLDataSource) {
structSeparator = ((SQLDataSource) dataSource).getSQLDialect().getStructSeparator();
}
StringBuilder query = new StringBuilder();
boolean hasPrevIdentifier = false;
for (DBDAttributeBinding attribute = this; attribute != null; attribute = attribute.getParentObject()) {
if (attribute.isPseudoAttribute() || attribute.getDataKind() == DBPDataKind.DOCUMENT) {
// Skip pseudo attributes and document attributes (e.g. Mongo root document)
continue;
}
if (hasPrevIdentifier) {
query.insert(0, structSeparator);
}
query.insert(0, DBUtils.getQuotedIdentifier(dataSource, attribute.getName()));
hasPrevIdentifier = true;
}
return query.toString();
}
@Override
public boolean isPersisted() {
return false;
}
/**
* Get parent by level.
* @param grand 0 - self, 1 - direct parent, 2 - grand parent, etc
* @return parent or null
*/
@Nullable
public DBDAttributeBinding getParent(int grand) {
if (grand == 0) {
return this;
}
DBDAttributeBinding p = this;
for (int i = 0; i < grand; i++) {
if (p == null) {
throw new IllegalArgumentException("Bad parent depth: " + grand);
}
p = p.getParentObject();
}
return p;
}
@NotNull
public DBDAttributeBinding getTopParent() {
for (DBDAttributeBinding binding = this; ; binding = binding.getParentObject()) {
if (binding.getParentObject() == null) {
return binding;
}
}
}
/**
* Attribute level. Zero based
* @return attribute level (depth)
*/
public int getLevel() {
if (getParentObject() == null) {
return 0;
}
int level = 0;
for (DBDAttributeBinding binding = getParentObject(); binding != null; binding = binding.getParentObject()) {
level++;
}
return level;
}
@Nullable
@Override
public DBSDataType getDataType() {
DBSEntityAttribute attribute = getEntityAttribute();
if (attribute instanceof DBSTypedObjectEx) {
return ((DBSTypedObjectEx) attribute).getDataType();
}
return null;
}
public void lateBinding(@NotNull DBCSession session, List<Object[]> rows) throws DBException {
DBSAttributeBase attribute = getAttribute();
final DBDAttributeTransformer[] transformers = DBVUtils.findAttributeTransformers(this, null);
if (transformers != null) {
session.getProgressMonitor().subTask("Transform attribute '" + attribute.getName() + "'");
final Map<String, String> transformerOptions = DBVUtils.getAttributeTransformersOptions(this);
for (DBDAttributeTransformer transformer : transformers) {
transformer.transformAttribute(session, this, rows, transformerOptions);
}
}
}
@Override
public String toString() {
return getName() + " [" + getOrdinalPosition() + "]";
}
}