}
public Expression visitUnboundCall(UnboundCall call) {
TemplateName calleeName = call.getCallee();
Map<String, Attribute> params = call.getAttributes();
Callable callee = params.containsKey(Implementable.INSTANCE_PARAM_NAME)
? serviceDirectory.getInstanceCallable(calleeName)
: serviceDirectory.getCallable(calleeName);
if (callee == null) {
alertSink.add(new CallableNotFoundError(call, calleeName));
return new StringConstant(call, null, "");
} else {
final ImmutableMap.Builder<String, Attribute> newAttrBuilder = ImmutableMap.builder();
// construct a Map of attribute bundles with one entry for each bundle parameter
final Map<String, Map<AttributeValidator, Attribute>> attrBundles
= Maps.newLinkedHashMap();
for (FormalParameter parameter : callee.getParameters()) {
if (parameter.getType() instanceof BundleType) {
attrBundles.put(parameter.getPrimaryName(),
new LinkedHashMap<AttributeValidator, Attribute>());
}
}
for (final Map.Entry<String, Attribute> param : params.entrySet()) {
final String name = param.getKey();
final FormalParameter parameter = callee.getParameter(name);
Attribute attr = param.getValue();
if (parameter == null) {
alertSink.add(new BadParameterError(attr.getValue(), callee, name));
continue;
}
// TODO(harryh): maybe better to use a DefaultingExpressionVisitor
// here?
if (attr.getValue() instanceof ObjectConstant) {
ObjectConstant oc = (ObjectConstant) attr.getValue();
// TODO(harryh): maybe this should be in Validator?
if (!parameter.regexMatches(oc)) {
alertSink.add(new InvalidParameterFailedRegexError(
calleeName, name, parameter.getRegex(), oc));
}
attr = parameter.hasConstructor()
? attr.withValue(new ConstructedConstant(oc, oc.getValue(), callee, parameter))
: attr.withValue(parameter.getType().parseObjectConstant(name, oc, alertSink));
}
attr = attr.withValue(prepareExpressionAsParameterValue(parameter, attr.getValue()));
final Attribute updatedAttr = visitAttribute(attr);
parameter.getType().acceptTypeVisitor(new DefaultingTypeVisitor<Void>() {
protected Void defaultVisitType(Type type) {
newAttrBuilder.put(name, updatedAttr);
return null;
}
public Void visitBundleType(BundleType type) {
final AttributeValidator validator = type.getValidator(name);
String innerContentTypeString = validator.getContentType();
if (innerContentTypeString != null) {
Schema innerSchema = schemaFactory.fromContentTypeName(innerContentTypeString);
attrBundles.get(parameter.getPrimaryName()).put(validator,
updatedAttr.withInnerSchema(innerSchema));
} else {
attrBundles.get(parameter.getPrimaryName()).put(validator, updatedAttr);
}
return null;
}
});
}
// go through the attrBundleMap and turn each entry into an
// AttrBundleParam and put this into the builder map.
for (Map.Entry<String, Map<AttributeValidator, Attribute>> attrBundle :
attrBundles.entrySet()) {
FormalParameter parameter = callee.getParameterByPrimary(attrBundle.getKey());
BundleType bt = (BundleType) parameter.getType();
// special case for the (common case) of a single bundle on the
// callee side. In this case there is no mixing of attributes
// between bundles so the GxpAttrBundleBuilder does not need to
// include only some attributes from passed in bundles. See the
// empty constructor in j/c/g/gxp/base/GxpAttrBundleBuilder.java
Set<String> includeAttrs = (attrBundles.size() == 1)
? Collections.<String>emptySet() : bt.getAttrMap().keySet();
AttrBundleParam newBundle =
new AttrBundleParam(call, callee.getSchema(), includeAttrs,
attrBundle.getValue(), call.getAttrBundles());
newAttrBuilder.put(attrBundle.getKey(),
new Attribute(call, attrBundle.getKey(),
newBundle, null));
}
// Handle content parameter
FormalParameter contentParam = callee.getContentConsumingParameter();
Expression content = prepareExpressionAsParameterValue(contentParam,
apply(call.getContent()));
boolean contentIgnorable = content.alwaysOnlyWhitespace();
if (contentParam == null) {
if (!contentIgnorable) {