package act.inject.param;
/*-
* #%L
* ACT Framework
* %%
* Copyright (C) 2014 - 2017 ActFramework
* %%
* 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.
* #L%
*/
import act.Act;
import act.app.ActionContext;
import act.app.App;
import act.inject.DependencyInjector;
import act.util.ActContext;
import org.osgl.inject.BeanSpec;
import org.osgl.mvc.result.BadRequest;
import org.osgl.mvc.util.Binder;
import org.osgl.util.S;
import org.osgl.util.StringValueResolver;
import java.lang.reflect.Type;
import java.util.*;
class CollectionLoader implements ParamValueLoader {
private final ParamKey key;
private final Class<? extends Collection> collectionClass;
private final Type elementType;
private final DependencyInjector<?> injector;
private final StringValueResolver resolver;
private final Binder binder;
private final Map<ParamKey, ParamValueLoader> childLoaders = new HashMap<ParamKey, ParamValueLoader>();
private final ParamValueLoaderService manager;
private final boolean isChar;
private final BeanSpec targetSpec;
CollectionLoader(
ParamKey key,
Class<? extends Collection> collection,
Type elementType,
BeanSpec targetSpec,
DependencyInjector<?> injector,
ParamValueLoaderService manager
) {
this.key = key;
this.collectionClass = collection;
this.elementType = elementType;
this.isChar = char.class == elementType || Character.class == elementType;
this.injector = injector;
this.manager = manager;
App app = Act.app();
Class<?> rawType = BeanSpec.rawTypeOf(elementType);
this.binder = app.binderManager().binder(rawType);
this.targetSpec = targetSpec;
if (null == binder) {
this.resolver = App.instance().resolverManager().resolver(rawType, targetSpec);
} else {
this.resolver = null;
}
// if (null == this.binder && null == this.resolver) {
// throw new IllegalArgumentException(S.fmt("Cannot find binder and resolver for %s", elementType));
// }
}
@Override
public String bindName() {
return key.toString();
}
@Override
public Object load(Object bean, ActContext<?> context, boolean noDefaultValue) {
ParamTree tree = ParamValueLoaderService.ensureParamTree(context);
ParamTreeNode node = tree.node(key);
if (null == node) {
return noDefaultValue ? null : injector.get(collectionClass);
}
Collection collection = null == bean ? injector.get(collectionClass) : (Collection) bean;
if (node.isList()) {
List<ParamTreeNode> nodes = node.list();
if (nodes.size() > 0) {
String value = nodes.get(0).value();
//if (S.notBlank(value)) {
for (int i = 0; i < nodes.size(); ++i) {
ParamTreeNode elementNode = nodes.get(i);
if (!elementNode.isLeaf()) {
throw new BadRequest("cannot parse param: expect leaf node, found: \n%s", node.debug());
}
context.attribute(ActionContext.ATTR_CURRENT_FILE_INDEX, i);
if (null != binder) {
collection.add(binder.resolve(null, elementNode.value(), context));
} else {
collection.add(resolver.resolve(elementNode.value()));
}
}
//}
}
} else if (node.isMap()) {
Set<String> childrenKeys = node.mapKeys();
for (String s : childrenKeys) {
int id;
try {
id = Integer.parseInt(s);
} catch (NumberFormatException e) {
throw new BadRequest("cannot parse param: list index is not a number: %s", s);
}
ParamTreeNode child = node.child(s);
if (child.isLeaf()) {
if (null != child.value()) {
if (null != binder) {
addToCollection(collection, id, binder.resolve(null, child.value(), context));
} else {
addToCollection(collection, id, resolver.resolve(child.value()));
}
}
} else {
ParamValueLoader childLoader = childLoader(child.key());
addToCollection(collection, id, childLoader.load(null, context, false));
}
}
} else {
resolveInto(collection, node.value(), context);
}
return collection;
}
private ParamValueLoader childLoader(ParamKey key) {
ParamValueLoader loader = childLoaders.get(key);
if (null == loader) {
loader = buildChildLoader(key);
childLoaders.put(key, loader);
}
return loader;
}
private ParamValueLoader buildChildLoader(ParamKey key) {
return manager.buildLoader(key, elementType, targetSpec);
}
private static void addToCollection(Collection collection, int index, Object bean) {
if (collection instanceof List) {
addToList((List) collection, index, bean);
} else {
collection.add(bean);
}
}
private static void addToList(List list, int index, Object bean) {
while (list.size() < index + 1) {
list.add(null);
}
list.set(index, bean);
}
private void resolveInto(Collection collection, String value, ActContext context) {
if (S.blank(value)) {
return;
}
// support multiple path variables like /foo/id1,id2
String[] sa = value.split("[,;]+");
boolean isChar = this.isChar;
for (String s : sa) {
if (isChar) {
char[] ca = s.toCharArray();
for (char c: ca) {
collection.add(c);
}
} else {
if (null != binder) {
collection.add(binder.resolve(null, s, context));
} else {
collection.add(resolver.resolve(s));
}
}
}
}
}