/*****************************************************************
* 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.di.spi;
import org.apache.cayenne.di.DIRuntimeException;
import org.apache.cayenne.di.Key;
import org.apache.cayenne.di.ListBuilder;
import org.apache.cayenne.di.Provider;
import org.apache.cayenne.di.Scope;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
/**
* @since 3.1
*/
class DefaultListBuilder<T> implements ListBuilder<T> {
protected static AtomicLong incrementer = new AtomicLong();
protected DefaultInjector injector;
protected Key<List<T>> bindingKey;
DefaultListBuilder(Key<List<T>> bindingKey, DefaultInjector injector) {
this.injector = injector;
this.bindingKey = bindingKey;
// trigger initialization of the ListProvider right away, as we need to bind an
// empty list even if the user never calls 'put'
getListProvider();
}
@Override
public ListBuilder<T> add(Class<? extends T> interfaceType) {
Provider<? extends T> provider = getProvider(interfaceType);
getListProvider().add(Key.get(interfaceType), provider);
return this;
}
@Override
public ListBuilder<T> addAfter(Class<? extends T> interfaceType, Class<? extends T> afterType) {
Provider<? extends T> provider = getProvider(interfaceType);
getListProvider().addAfter(Key.get(interfaceType), provider, Key.get(afterType));
return this;
}
@Override
public ListBuilder<T> insertBefore(Class<? extends T> interfaceType, Class<? extends T> beforeType) {
Provider<? extends T> provider = getProvider(interfaceType);
getListProvider().insertBefore(Key.get(interfaceType), provider, Key.get(beforeType));
return this;
}
@SuppressWarnings("unchecked")
@Override
public ListBuilder<T> add(T value) {
Key<? extends T> key = Key.get((Class<? extends T>)value.getClass(),
String.valueOf(incrementer.getAndIncrement()));
getListProvider().add(key, createProvider(value));
return this;
}
@SuppressWarnings("unchecked")
@Override
public ListBuilder<T> addAfter(T value, Class<? extends T> afterType) {
Key<? extends T> key = Key.get((Class<? extends T>)value.getClass(),
String.valueOf(incrementer.getAndIncrement()));
getListProvider().addAfter(key, createProvider(value), Key.get(afterType));
return this;
}
@SuppressWarnings("unchecked")
@Override
public ListBuilder<T> insertBefore(T value, Class<? extends T> beforeType) {
Key<? extends T> key = Key.get((Class<? extends T>)value.getClass(),
String.valueOf(incrementer.getAndIncrement()));
getListProvider().insertBefore(key, createProvider(value), Key.get(beforeType));
return this;
}
@Override
public ListBuilder<T> addAll(Collection<T> values) {
getListProvider().addAll(createProviderMap(values));
return this;
}
@Override
public ListBuilder<T> addAllAfter(Collection<T> values, Class<? extends T> afterType) {
getListProvider().addAllAfter(createProviderMap(values), Key.get(afterType));
return this;
}
@Override
public ListBuilder<T> insertAllBefore(Collection<T> values, Class<? extends T> beforeType) {
getListProvider().insertAllBefore(createProviderMap(values), Key.get(beforeType));
return this;
}
private Provider<? extends T> getProvider(Class<? extends T> interfaceType)
throws DIRuntimeException {
Key<? extends T> key = Key.get(interfaceType);
Binding<? extends T> binding = injector.getBinding(key);
if (binding == null) {
return addWithBinding(interfaceType);
}
return binding.getScoped();
}
@SuppressWarnings("unchecked")
private Provider<T> createProvider(T value) {
Provider<T> provider0 = new InstanceProvider<>(value);
return new FieldInjectingProvider<>(provider0, injector);
}
private Map<Key<? extends T>, Provider<? extends T>> createProviderMap(Collection<T> objects) {
Map<Key<? extends T>, Provider<? extends T>> keyProviderMap = new LinkedHashMap<>();
for (T object : objects) {
Provider<T> provider0 = new InstanceProvider<>(object);
Provider<T> provider1 = new FieldInjectingProvider<>(provider0, injector);
@SuppressWarnings("unchecked")
Class<? extends T> objectType = (Class<? extends T>)object.getClass();
keyProviderMap.put(Key.get(objectType, String.valueOf(incrementer.getAndIncrement())), provider1);
}
return keyProviderMap;
}
private <K extends T> Provider<? extends T> addWithBinding(Class<K> interfaceType) {
Key<K> key = Key.get(interfaceType);
Provider<K> provider0 = new ConstructorInjectingProvider<>(interfaceType, injector);
Provider<K> provider1 = new FieldInjectingProvider<>(provider0, injector);
injector.putBinding(key, provider1);
return injector.getProvider(key);
}
private ListProvider<T> getListProvider() {
ListProvider<T> provider;
Binding<List<T>> binding = injector.getBinding(bindingKey);
if (binding == null) {
provider = new ListProvider<>();
injector.putBinding(bindingKey, provider);
} else {
provider = (ListProvider<T>) binding.getOriginal();
}
return provider;
}
@Override
public void in(Scope scope) {
injector.changeBindingScope(bindingKey, scope);
}
}