/*****************************************************************
* 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 org.apache.cayenne.access.jdbc.reader;
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.access.jdbc.ColumnDescriptor;
import org.apache.cayenne.access.jdbc.RowDescriptor;
import org.apache.cayenne.access.jdbc.reader.DataRowPostProcessor.ColumnOverride;
import org.apache.cayenne.access.types.ExtendedType;
import org.apache.cayenne.access.types.ExtendedTypeMap;
import org.apache.cayenne.dba.DbAdapter;
import org.apache.cayenne.dba.TypesMapping;
import org.apache.cayenne.map.Entity;
import org.apache.cayenne.map.ObjAttribute;
import org.apache.cayenne.query.EntityResultSegment;
import org.apache.cayenne.query.QueryMetadata;
import org.apache.cayenne.query.ScalarResultSegment;
import org.apache.cayenne.reflect.ClassDescriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* @since 4.0
*/
public class DefaultRowReaderFactory implements RowReaderFactory {
@Override
public RowReader<?> rowReader(RowDescriptor descriptor, QueryMetadata queryMetadata, DbAdapter adapter,
Map<ObjAttribute, ColumnDescriptor> attributeOverrides) {
PostprocessorFactory postProcessorFactory = new PostprocessorFactory(descriptor, queryMetadata,
adapter.getExtendedTypes(), attributeOverrides);
List<Object> rsMapping = queryMetadata.getResultSetMapping();
if (rsMapping == null) {
return createFullRowReader(descriptor, queryMetadata, postProcessorFactory);
}
int resultWidth = rsMapping.size();
if (resultWidth == 0) {
throw new CayenneRuntimeException("Empty result descriptor");
}
if (queryMetadata.isSingleResultSetMapping()) {
Object segment = rsMapping.get(0);
if (segment instanceof EntityResultSegment) {
return createEntityRowReader(descriptor, queryMetadata, (EntityResultSegment) segment,
postProcessorFactory);
} else {
return new ScalarRowReader<Object>(descriptor, (ScalarResultSegment) segment);
}
} else {
CompoundRowReader reader = new CompoundRowReader(resultWidth);
for (int i = 0; i < resultWidth; i++) {
Object segment = rsMapping.get(i);
if (segment instanceof EntityResultSegment) {
reader.addRowReader(
i,
createEntityRowReader(descriptor, queryMetadata, (EntityResultSegment) segment,
postProcessorFactory));
} else {
reader.addRowReader(i, new ScalarRowReader<>(descriptor, (ScalarResultSegment) segment));
}
}
return reader;
}
}
private RowReader<?> createEntityRowReader(RowDescriptor descriptor, QueryMetadata queryMetadata,
EntityResultSegment resultMetadata, PostprocessorFactory postProcessorFactory) {
if (queryMetadata.getPageSize() > 0) {
return new IdRowReader<Object>(descriptor, queryMetadata, resultMetadata, postProcessorFactory.get());
} else if (resultMetadata.getClassDescriptor() != null && resultMetadata.getClassDescriptor().hasSubclasses()) {
return new InheritanceAwareEntityRowReader(descriptor, resultMetadata, postProcessorFactory.get());
} else {
return new EntityRowReader(descriptor, resultMetadata, postProcessorFactory.get());
}
}
private RowReader<?> createFullRowReader(RowDescriptor descriptor, QueryMetadata queryMetadata,
PostprocessorFactory postProcessorFactory) {
if (queryMetadata.getPageSize() > 0) {
return new IdRowReader<Object>(descriptor, queryMetadata, null, postProcessorFactory.get());
} else if (queryMetadata.getClassDescriptor() != null && queryMetadata.getClassDescriptor().hasSubclasses()) {
return new InheritanceAwareRowReader(descriptor, queryMetadata, postProcessorFactory.get());
} else {
return new FullRowReader(descriptor, queryMetadata, postProcessorFactory.get());
}
}
private class PostprocessorFactory {
private QueryMetadata queryMetadata;
private ExtendedTypeMap extendedTypes;
private Map<ObjAttribute, ColumnDescriptor> attributeOverrides;
private RowDescriptor rowDescriptor;
private boolean created;
private DataRowPostProcessor postProcessor;
PostprocessorFactory(RowDescriptor rowDescriptor, QueryMetadata queryMetadata, ExtendedTypeMap extendedTypes,
Map<ObjAttribute, ColumnDescriptor> attributeOverrides) {
this.rowDescriptor = rowDescriptor;
this.extendedTypes = extendedTypes;
this.attributeOverrides = attributeOverrides;
this.queryMetadata = queryMetadata;
}
DataRowPostProcessor get() {
if (!created) {
postProcessor = create();
created = true;
}
return postProcessor;
}
private DataRowPostProcessor create() {
if (attributeOverrides.isEmpty()) {
return null;
}
ColumnDescriptor[] columns = rowDescriptor.getColumns();
Map<String, Collection<ColumnOverride>> columnOverrides = new HashMap<>(2);
for (Entry<ObjAttribute, ColumnDescriptor> entry : attributeOverrides.entrySet()) {
ObjAttribute attribute = entry.getKey();
Entity entity = attribute.getEntity();
String key = null;
int jdbcType = TypesMapping.NOT_DEFINED;
int index = -1;
for (int i = 0; i < columns.length; i++) {
if (columns[i] == entry.getValue()) {
// if attribute type is the same as column, there is no
// conflict
if (!attribute.getType().equals(columns[i].getJavaClass())) {
// note that JDBC index is "1" based
index = i + 1;
jdbcType = columns[i].getJdbcType();
key = columns[i].getDataRowKey();
}
break;
}
}
if (index < 1) {
continue;
}
ExtendedType converter = extendedTypes.getRegisteredType(attribute.getType());
Collection<ColumnOverride> overrides = columnOverrides.get(entity.getName());
if (overrides == null) {
overrides = new ArrayList<>(3);
columnOverrides.put(entity.getName(), overrides);
}
overrides.add(new ColumnOverride(index, key, converter, jdbcType));
}
// inject null post-processor
if (columnOverrides.isEmpty()) {
return null;
}
ClassDescriptor rootDescriptor = queryMetadata.getClassDescriptor();
return new DataRowPostProcessor(rootDescriptor, columnOverrides);
}
}
}