Package com.linkedin.data.transform

Source Code of com.linkedin.data.transform.ProjectionUtil$PathSpecFilter

/*
* Copyright (c) 2014 LinkedIn Corp.
*
*    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.
*/

package com.linkedin.data.transform;


import com.linkedin.data.DataMap;
import com.linkedin.data.Null;
import com.linkedin.data.schema.PathSpec;
import com.linkedin.data.transform.filter.CopyFilter;
import com.linkedin.data.transform.filter.request.MaskTree;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


/**
* @author Keren Jin
*/
public class ProjectionUtil
{
  private static class PathSpecFilter extends CopyFilter
  {
    /**
     * The only error supposed to be received here is where the value is primitive but filter is complex
     * This should be ignored to allow a prefix to stay.
     */
    @Override
    protected Object onError(Object field, String format, Object... args)
    {
      return null;
    }
  }

  /**
   * <p>Given a {@link MaskTree} and a {@link PathSpec}, return if the PathSpec is present.
   * In other words, it tells that when Rest.li projects on a record with the given MaskTree, whether the field
   * represented by the PathSpec would survive.</p>
   *
   * Convenient version of getPresentPaths() to only test one {@link PathSpec} at a time.
   * For more details, check the documentation of getPresentPaths().
   *
   * @param filter filter to apply
   * @param path PathSpec to test
   * @return true if the PathSpec is present in the MaskTree filtering
   */
  public static boolean isPathPresent(MaskTree filter, PathSpec path)
  {
    return !getPresentPaths(filter, Collections.singleton(path)).isEmpty();
  }

  /**
   * <p>Given a {@link MaskTree} and a {@link Set} of {@link PathSpec}s, return the PathSpecs that are present.
   * In other words, it tells that when Rest.li projects on a record with the given MaskTree, whether the fields
   * represented by the PathSpecs would survive.</p>
   *
   * <p>If a PathSpec is a prefix of the filter or the filter is a prefix of a PathSpec, it is always considered present. For example,
   * <pre>
   * MaskTree:  /foo/bar:     POSITIVE
   * PathSpecs: /foo:         present
   *            /foo/bar:     present
   *            /foo/bar/baz: present
   *            /xyz:         not present
   * </pre>
   * </p>
   *
   * <p>Empty filter Empty PathSpec is considered prefix of all filters.</p>
   *
   * @param filter filter to apply
   * @param paths PathSpecs to test
   * @return PathSpecs that are present in the MaskTree filtering
   */
  public static Set<PathSpec> getPresentPaths(MaskTree filter, Set<PathSpec> paths)
  {
    // this emulates the behavior of Rest.li server
    // if client does not specify any mask, the server receives null when retrieving the MaskTree
    // in this case, all fields are returned
    if (filter == null)
    {
      return paths;
    }

    final DataMap filterMap = filter.getDataMap();
    if (filter.getDataMap().isEmpty())
    {
      return Collections.emptySet();
    }

    final DataMap pathSpecMap = createPathSpecMap(paths);

    @SuppressWarnings("unchecked")
    final DataMap filteredPathSpecs = (DataMap) new PathSpecFilter().filter(pathSpecMap, filterMap);

    return validate(filteredPathSpecs, paths);
  }

  private static DataMap createPathSpecMap(Set<PathSpec> paths)
  {
    final DataMap pathSpecMap = new DataMap();

    for (PathSpec p : paths)
    {
      final List<String> components = p.getPathComponents();
      DataMap currentMap = pathSpecMap;

      for (int i = 0; i < components.size(); ++i)
      {
        final String currentComponent = components.get(i);
        final Object currentValue = currentMap.get(currentComponent);

        if (i < components.size() - 1)
        {
          if (currentValue instanceof DataMap)
          {
            @SuppressWarnings("unchecked")
            final DataMap valueMap = (DataMap) currentValue;
            currentMap = valueMap;
          }
          else
          {
            final DataMap newMap = new DataMap();
            currentMap.put(currentComponent, newMap);
            currentMap = newMap;
          }
        }
        else if (currentValue == null)
        {
          currentMap.put(currentComponent, Null.getInstance());
        }
      }
    }

    return pathSpecMap;
  }

  private static Set<PathSpec> validate(DataMap filteredPathSpecs, Set<PathSpec> paths)
  {
    final Set<PathSpec> result = new HashSet<PathSpec>();

    for (PathSpec p : paths)
    {
      final List<String> components = p.getPathComponents();
      DataMap currentMap = filteredPathSpecs;
      boolean isPresent = true;

      for (int i = 0; i < components.size(); ++i)
      {
        final String currentComponent = components.get(i);
        final Object currentValue = currentMap.get(currentComponent);

        if (currentValue instanceof DataMap)
        {
          @SuppressWarnings("unchecked")
          final DataMap valueMap = (DataMap) currentValue;
          currentMap = valueMap;
        }
        else
        {
          isPresent = currentMap.containsKey(currentComponent);
          break;
        }
      }

      if (isPresent)
      {
        result.add(p);
      }
    }

    return result;
  }
}
TOP

Related Classes of com.linkedin.data.transform.ProjectionUtil$PathSpecFilter

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.