Search in sources :

Example 1 with ListMap

use of com.linkedin.restli.internal.common.PathSegment.ListMap in project rest.li by linkedin.

the class QueryParamsDataMap method parseDataMapKeys.

/**
   * Parse a multi-map representing query parameters into a DataMap, as follows.
   *
   * Multi-indexed parameter names, such as ids[0][2] or ids[0,2] are not
   * currently supported.
   *
   * For example, the following query string:
   *
   * /groupMemberships?ids[0].params.versionTag=tag1&ids[0].params.authToken=tok1&ids[0].memberID=1&ids[0].groupID=2& \
   *                    ids[1].params.versionTag=tag2&ids[1].params.authToken=tok2&ids[1].memberID=2&ids[1].groupID=2& \
   *                    q=someFinder
   *
   * is parsed into the following data map:
   *
   * {"ids" : [
   *    {
   *        "memberID"  : "1",
   *        "groupID"   : "2",
   *        "params"    : {
   *            "authToken" : "tok1",
   *            "versionTag" : "tag1"
   *        }
   *    },
   *    {   "memberID"  : "2",
   *        "groupID"   : "2",
   *        "params"    : {
   *            "authToken" : "tok2",
   *            "versionTag" : "tag2"
   *        }
   *    }
   *  ],
   *  "q" : "someFinder"
   * }
   *
   *
   * Note: at this point the data map is not typed - all names and values are
   * parsed as strings.
   *
   * Note: when parsing indexed parameter names, those will be converted to a list,
   * preserving the order of the values according to the index, but ignoring any
   * possible "holes" in the index sequence. The index values therefore only
   * serve to define order of the parameter values, rather than their actual
   * position in any collection.
   *
   * @param queryParameters the query parameters
   * @return - the DataMap represented by potentially hierarchical keys encoded
   * by the multi-part parameter names.
   *
   * @throws PathSegmentSyntaxException
   */
public static DataMap parseDataMapKeys(Map<String, List<String>> queryParameters) throws PathSegmentSyntaxException {
    // The parameters are parsed into an intermediary structure comprised of
    // HashMap<String,Object> and HashMap<Integer,Object>, defined respectively
    // as MapMap and ListMap for convenience. This is done for two reasons:
    // - first, indexed keys representing lists are parsed into ListMaps keyed on
    //   index values, since the indices may come in any order in the query parameter,
    //   while we want to preserve the order.
    // - second, DataMap only accepts Data objects as values, so ListMaps cannot
    //   be stored there, so using an intermediary structure even for maps.
    MapMap dataMap = new MapMap();
    for (Map.Entry<String, List<String>> entry : queryParameters.entrySet()) {
        // As per the notation above, we no longer support multiple occurrences of
        // a parameter (considering its full multi-part and indexed name), i.e
        // there should be only a single entry in each list. For backward compatibility
        // as well as ease of use, repeated parameters are still allowed if they
        // are "simple", i.e. they are not multi-part or indexed.
        List<String> valueList = entry.getValue();
        if (valueList.size() == 1) {
            String[] key = SEGMENT_DELIMITER_PATTERN.split(entry.getKey());
            parseParameter(key, valueList.get(0), dataMap);
        } else {
            String parameterName = entry.getKey();
            // indexed and then simulate the index for each one.
            if (parameterName.indexOf('.') != -1)
                throw new PathSegmentSyntaxException("Multiple values of complex query parameter are not supported");
            if (parameterName.charAt(parameterName.length() - 1) == ']')
                throw new PathSegmentSyntaxException("Multiple values of indexed query parameter are not supported");
            if (dataMap.containsKey(parameterName))
                throw new PathSegmentSyntaxException("Conflicting references to key " + parameterName + "[0]");
            else {
                dataMap.put(parameterName, new DataList(valueList));
            }
        }
    }
    return (DataMap) convertToDataCollection(dataMap);
}
Also used : MapMap(com.linkedin.restli.internal.common.PathSegment.MapMap) DataList(com.linkedin.data.DataList) PathSegmentSyntaxException(com.linkedin.restli.internal.common.PathSegment.PathSegmentSyntaxException) ArrayList(java.util.ArrayList) DataList(com.linkedin.data.DataList) List(java.util.List) ByteString(com.linkedin.data.ByteString) HashMap(java.util.HashMap) ListMap(com.linkedin.restli.internal.common.PathSegment.ListMap) DataMap(com.linkedin.data.DataMap) Map(java.util.Map) MapMap(com.linkedin.restli.internal.common.PathSegment.MapMap) DataMap(com.linkedin.data.DataMap)

Example 2 with ListMap

use of com.linkedin.restli.internal.common.PathSegment.ListMap in project rest.li by linkedin.

the class QueryParamsDataMap method convertToDataCollection.

/**
   * The method recursively traverses the input Map and transforms it as follows:
   * - wherever encounters instances of ListMap (Map<Integer,Object>), converts
   *   those to DataList, preserving key order but ignoring any "holes" in key sequences.
   * - wherever encounters instances of MapMap (Map<String,Object>) converts them
   *   into DataMap.
   *
   * This is done since while parsing out indexed query parameters it's convenient to
   * parse them into a map due to arbitrary order in which they may appear, while if they
   * are defined in the schema as a list, a DataList is expected during validation.
   *
   * @param map the Map to transform
   * @return DataMap or DataList, depending on the type of the input Map
   */
private static DataComplex convertToDataCollection(Map<?, ?> map) {
    // recursively on every value that is itself a map
    if (map instanceof MapMap) {
        DataMap result = new DataMap();
        MapMap mapMap = (MapMap) map;
        for (Entry<String, Object> entry : mapMap.entrySet()) {
            Object value = entry.getValue();
            if (value instanceof Map<?, ?>)
                value = convertToDataCollection((Map<?, ?>) value);
            result.put(entry.getKey(), value);
        }
        return result;
    }
    // convert this map into a list preserving key order.
    if (map instanceof ListMap) {
        DataList result = new DataList();
        ListMap listMap = (ListMap) map;
        List<Integer> sortedKeys = new ArrayList<Integer>(listMap.keySet());
        Collections.sort(sortedKeys);
        for (Integer key : sortedKeys) {
            Object object = map.get(key);
            if (object instanceof Map<?, ?>)
                object = convertToDataCollection((Map<?, ?>) object);
            result.add(object);
        }
        return result;
    }
    throw new IllegalArgumentException("Only MapMap or ListMap input argument types are allowed");
}
Also used : MapMap(com.linkedin.restli.internal.common.PathSegment.MapMap) DataList(com.linkedin.data.DataList) ArrayList(java.util.ArrayList) ByteString(com.linkedin.data.ByteString) HashMap(java.util.HashMap) ListMap(com.linkedin.restli.internal.common.PathSegment.ListMap) DataMap(com.linkedin.data.DataMap) Map(java.util.Map) MapMap(com.linkedin.restli.internal.common.PathSegment.MapMap) ListMap(com.linkedin.restli.internal.common.PathSegment.ListMap) DataMap(com.linkedin.data.DataMap)

Aggregations

ByteString (com.linkedin.data.ByteString)2 DataList (com.linkedin.data.DataList)2 DataMap (com.linkedin.data.DataMap)2 ListMap (com.linkedin.restli.internal.common.PathSegment.ListMap)2 MapMap (com.linkedin.restli.internal.common.PathSegment.MapMap)2 ArrayList (java.util.ArrayList)2 HashMap (java.util.HashMap)2 Map (java.util.Map)2 PathSegmentSyntaxException (com.linkedin.restli.internal.common.PathSegment.PathSegmentSyntaxException)1 List (java.util.List)1