/*
* Copyright 2007 The Fornax Project Team, including 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 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.sculptor.framework.accessimpl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* @author Patrik Nordwall
*
*/
public abstract class ChunkFetcherBase<T, KEY> {
// max for Oracle is 1000
private static final int CHUNK_SIZE = 990;
private final String restrictionPropertyName;
/**
* @param restrictionPropertyName
* the name of the property to use for the 'in' criteria
*/
public ChunkFetcherBase(String restrictionPropertyName) {
this.restrictionPropertyName = restrictionPropertyName;
}
protected String getRestrictionPropertyName() {
return restrictionPropertyName;
}
public Map<KEY, T> getDomainObjects(Set<? extends KEY> keys) {
return filterResult(keys, getDomainObjectsAsList(keys));
}
public Map<KEY, Set<T>> getDomainObjectsNonUniqueKeys(Set<? extends KEY> keys) {
return filterResultNonUniqueKeys(keys, getDomainObjectsAsList(keys));
}
/**
* Fetch existing domain objects based on natural keys
*
* @param keys
* Set of natural keys for the domain objects to fetch
* @param resultAsSet
* indicates if the keys are unique or not, eg. the resulting map
* must be a set of objects.
* @return Map with keys and domain objects
*/
public List<T> getDomainObjectsAsList(Set<? extends KEY> keys) {
// it is not "possible" to use huge number of parameters in a
// Restrictions.in criterion and therefore we chunk the query
// into pieces
List<T> all = new ArrayList<T>();
Iterator<? extends KEY> iter = keys.iterator();
List<KEY> chunkKeys = new ArrayList<KEY>();
for (int i = 1; iter.hasNext(); i++) {
KEY element = iter.next();
chunkKeys.add(element);
if ((i % CHUNK_SIZE) == 0) {
all.addAll(getChunk(chunkKeys));
chunkKeys = new ArrayList<KEY>();
}
}
// and then the last part
if (!chunkKeys.isEmpty()) {
all.addAll(getChunk(chunkKeys));
}
return all;
}
protected Map<KEY, T> filterResult(Set<? extends KEY> keys, List<T> all) {
Map<KEY, T> existingObjectsMap = new HashMap<KEY, T>();
for (T obj : all) {
KEY key = key(obj);
if (keys.contains(key)) {
existingObjectsMap.put(key, obj);
}
}
return existingObjectsMap;
}
protected Map<KEY, Set<T>> filterResultNonUniqueKeys(Set<? extends KEY> keys, List<T> all) {
Map<KEY, Set<T>> existingObjectsMap = new HashMap<KEY, Set<T>>();
for (T obj : all) {
KEY key = key(obj);
if (keys.contains(key)) {
Set<T> keySet = existingObjectsMap.get(key);
if (keySet == null) {
keySet = new LinkedHashSet<T>();
existingObjectsMap.put(key, keySet);
}
keySet.add(obj);
}
}
return existingObjectsMap;
}
/**
* @return the natural key for the domain object, normally this is the value
* of the property specified in the constructor, but for composite
* keys it is not
*/
protected abstract KEY key(T obj);
protected abstract List<T> getChunk(Collection<KEY> keys);
/**
* By default, the values to use in the restriction criteria are the same as
* the key objects, but if it is composite keys, then the subclass must
* override and extract the property values to use in the restriction
* criteria.
*/
protected Collection<KEY> restrictionPropertyValues(Collection<KEY> keys) {
return keys;
}
}