/** * This method trims doc (1st arg) according to projected (2nd arg), which may have been modified * by projection. This method works in-place, meaning that the doc may be mutated. */ private static void trim(DataMap doc, DataMap projected) { DataMap toAddDoc = new DataMap(); Set<String> fields = doc.keySet(); List<String> toRemoveDoc = new ArrayList<String>(fields.size()); for (String f : fields) { Object v = doc.get(f); if (f.equals(PatchConstants.DELETE_COMMAND)) { DataList deletedFields = (DataList) v; DataList filteredDeleteFields = new DataList(); for (Object patchDeleteField : deletedFields) { if (projected.containsKey(patchDeleteField)) { filteredDeleteFields.add(patchDeleteField); } } toRemoveDoc.add(f); if (!filteredDeleteFields.isEmpty()) { toAddDoc.put(PatchConstants.DELETE_COMMAND, filteredDeleteFields); } } else if (f.equals(PatchConstants.SET_COMMAND)) { DataMap setFields = (DataMap) v; Set<String> setFieldNames = setFields.keySet(); List<String> toRemove = new LinkedList<String>(); DataMap filteredSetFields = new DataMap(); for (String setFieldName : setFieldNames) { if (projected.containsKey(setFieldName)) { filteredSetFields.put(setFieldName, projected.get(setFieldName)); } toRemove.add(setFieldName); } for (String fieldToRemove : toRemove) { setFields.remove(fieldToRemove); if (filteredSetFields.containsKey(fieldToRemove)) { setFields.put(fieldToRemove, filteredSetFields.get(fieldToRemove)); } } if (setFields.isEmpty()) { toRemoveDoc.add(f); } } else if (v instanceof DataMap) { if (projected.containsKey(f)) { trim((DataMap) v, (DataMap) projected.get(f)); } else { toRemoveDoc.add(f); } } } // apply changes to doc for (String f : toRemoveDoc) { doc.remove(f); } for (String f : toAddDoc.keySet()) { doc.put(f, toAddDoc.get(f)); } }
/** * Build arguments for resource method invocation. Combines various types of arguments into a * single array. * * @param positionalArguments pass-through arguments coming from {@link RestLiArgumentBuilder} * @param resourceMethod the resource method * @param context {@link ResourceContext} * @param template {@link DynamicRecordTemplate} * @return array of method argument for method invocation. */ @SuppressWarnings("deprecation") public static Object[] buildArgs( final Object[] positionalArguments, final ResourceMethodDescriptor resourceMethod, final ResourceContext context, final DynamicRecordTemplate template) { List<Parameter<?>> parameters = resourceMethod.getParameters(); Object[] arguments = Arrays.copyOf(positionalArguments, parameters.size()); fixUpComplexKeySingletonArraysInArguments(arguments); for (int i = positionalArguments.length; i < parameters.size(); ++i) { Parameter<?> param = parameters.get(i); try { if (param.getParamType() == Parameter.ParamType.KEY || param.getParamType() == Parameter.ParamType.ASSOC_KEY_PARAM) { Object value = context.getPathKeys().get(param.getName()); if (value != null) { arguments[i] = value; continue; } } else if (param.getParamType() == Parameter.ParamType.CALLBACK) { continue; } else if (param.getParamType() == Parameter.ParamType.PARSEQ_CONTEXT_PARAM || param.getParamType() == Parameter.ParamType.PARSEQ_CONTEXT) { continue; // don't know what to fill in yet } else if (param.getParamType() == Parameter.ParamType.HEADER) { HeaderParam headerParam = param.getAnnotations().get(HeaderParam.class); String value = context.getRequestHeaders().get(headerParam.value()); arguments[i] = value; continue; } // Since we have multiple different types of MaskTrees that can be passed into resource // methods, // we must evaluate based on the param type (annotation used) else if (param.getParamType() == Parameter.ParamType.PROJECTION || param.getParamType() == Parameter.ParamType.PROJECTION_PARAM) { arguments[i] = context.getProjectionMask(); continue; } else if (param.getParamType() == Parameter.ParamType.METADATA_PROJECTION_PARAM) { arguments[i] = context.getMetadataProjectionMask(); continue; } else if (param.getParamType() == Parameter.ParamType.PAGING_PROJECTION_PARAM) { arguments[i] = context.getPagingProjectionMask(); continue; } else if (param.getParamType() == Parameter.ParamType.CONTEXT || param.getParamType() == Parameter.ParamType.PAGING_CONTEXT_PARAM) { PagingContext ctx = RestUtils.getPagingContext(context, (PagingContext) param.getDefaultValue()); arguments[i] = ctx; continue; } else if (param.getParamType() == Parameter.ParamType.PATH_KEYS || param.getParamType() == Parameter.ParamType.PATH_KEYS_PARAM) { arguments[i] = context.getPathKeys(); continue; } else if (param.getParamType() == Parameter.ParamType.RESOURCE_CONTEXT || param.getParamType() == Parameter.ParamType.RESOURCE_CONTEXT_PARAM) { arguments[i] = context; continue; } else if (param.getParamType() == Parameter.ParamType.VALIDATOR_PARAM) { RestLiDataValidator validator = new RestLiDataValidator( resourceMethod.getResourceModel().getResourceClass().getAnnotations(), resourceMethod.getResourceModel().getValueClass(), resourceMethod.getMethodType()); arguments[i] = validator; continue; } else if (param.getParamType() == Parameter.ParamType.POST) { // handle action parameters if (template != null) { DataMap data = template.data(); if (data.containsKey(param.getName())) { arguments[i] = template.getValue(param); continue; } } } else if (param.getParamType() == Parameter.ParamType.QUERY) { Object value; if (DataTemplate.class.isAssignableFrom(param.getType())) { value = buildDataTemplateArgument(context, param); } else { value = buildRegularArgument(context, param); } if (value != null) { arguments[i] = value; continue; } } else if (param.getParamType() == Parameter.ParamType.BATCH || param.getParamType() == Parameter.ParamType.RESOURCE_KEY) { // should not come to this routine since it should be handled by passing in // positionalArguments throw new RoutingException( "Parameter '" + param.getName() + "' should be passed in as a positional argument", HttpStatus.S_400_BAD_REQUEST.getCode()); } else { // unknown param type throw new RoutingException( "Parameter '" + param.getName() + "' has an unknown parameter type '" + param.getParamType().name() + "'", HttpStatus.S_400_BAD_REQUEST.getCode()); } } catch (TemplateRuntimeException e) { throw new RoutingException( "Parameter '" + param.getName() + "' is invalid", HttpStatus.S_400_BAD_REQUEST.getCode()); } try { // Handling null-valued parameters not provided in resource context or entity body // check if it is optional parameter if (param.isOptional() && param.hasDefaultValue()) { arguments[i] = param.getDefaultValue(); } else if (param.isOptional() && !param.getType().isPrimitive()) { // optional primitive parameter must have default value or provided arguments[i] = null; } else { throw new RoutingException( "Parameter '" + param.getName() + "' is required", HttpStatus.S_400_BAD_REQUEST.getCode()); } } catch (ResourceConfigException e) { // Parameter default value format exception should result in server error code 500. throw new RestLiServiceException( HttpStatus.S_500_INTERNAL_SERVER_ERROR, "Parameter '" + param.getName() + "' default value is invalid", e); } } return arguments; }