public static <T extends RecordTemplate> PatchRequest<T> applyProjection(PatchRequest<T> patch, MaskTree projection)
  {
    try
    {
      /**
       * Implementation consists of 3 steps:
       * 1) move data conveyed by patch out of meta-commands (expose method)
       * 2) apply projection on it
       * 2) trim original patch with result of projection
       * In order to limit amount of generated garbage operations are
       * performed in-place
       */
      DataMap forProjecting = patch.getPatchDocument().copy();
      expose(forProjecting);
      DataMap projected = project(forProjecting, projection);
      DataMap forTrimming = patch.getPatchDocument().copy();
      trim(forTrimming, projected);
      return PatchRequest.createFromPatchDocument(forTrimming);
    }
    catch (CloneNotSupportedException e)
    {
      throw new IllegalArgumentException("Patch must be cloneable in order to apply projection to it", e);
    }
  }
@Test(dataProvider = "data")
  public void testProjectionOnPatch(String[] patchAndProjection, String expectedResult) throws IOException
  {
    DataMap patch = dataMapFromString(patchAndProjection[0].replace('\'', '"'));
    DataMap projection = dataMapFromString(patchAndProjection[1].replace('\'', '"'));
    DataMap expected = dataMapFromString(expectedResult.replace('\'', '"'));
    assertEquals(PatchHelper.applyProjection(PatchRequest.<RecordTemplate>createFromPatchDocument(patch), new MaskTree(projection)).getPatchDocument(), expected);
  }