('/**\\n   * Make a best effort at closing all the cached values. This method is *not* guaranteed to close\\n   * every cached value if there are concurrent users of the cache.  As a result, this method\\n   * should only be relied upon if only a single thread is using this cache while {@code #close} is\\n   * called.\\n   *\\n   * @throws IOException if any entry throws an IOException while closing. An entry throwing an\\n   *    IOException will prevent any further entries from being cleaned up.\\n   */', '')

('', '// Attempt to remove the entry from the cache\n')

('', '// If successfull, close the value\n')

b'/**\n   * Make a best effort at closing all the cached values. This method is *not* guaranteed to close\n   * every cached value if there are concurrent users of the cache.  As a result, this method\n   * should only be relied upon if only a single thread is using this cache while {@code #close} is\n   * called.\n   *\n   * @throws IOException if any entry throws an IOException while closing. An entry throwing an\n   *    IOException will prevent any further entries from being cleaned up.\n   */'@Override
  public void close() throws IOException {
    mIsOpen = false;
    for (Map.Entry<K, CacheEntry<V>> entry : mMap.entrySet()) {
      // Attempt to remove the entry from the cache
      if (mMap.remove(entry.getKey(), entry.getValue())) {
        // If successfull, close the value
        CacheEntry<V> cacheEntry = entry.getValue();
        synchronized (cacheEntry) {
          cacheEntry.getValue().close();
        }
      }
    }
  }

@Test(expected = IllegalStateException.class)
  public void closeableOnceFailsWhenDoubleClosed() throws Exception {
    Closeable closeable = new CloseableOnce<Object>(null);
    closeable.close();
    closeable.close();
  }


('/**\\n   * Constructs a split key file from an input stream.  This object will take ownership of\\n   * the inputStream, which you should clean up by calling close().\\n   *\\n   * @param inputStream The file contents.\\n   * @return the region boundaries, as a list of row keys.\\n   * @throws IOException on I/O error.\\n   */', '')

b'/**\n   * Constructs a split key file from an input stream.  This object will take ownership of\n   * the inputStream, which you should clean up by calling close().\n   *\n   * @param inputStream The file contents.\n   * @return the region boundaries, as a list of row keys.\n   * @throws IOException on I/O error.\n   */'public static List<byte[]> decodeRegionSplitList(InputStream inputStream) throws IOException {
    try {
      final String content =
          Bytes.toString(IOUtils.toByteArray(Preconditions.checkNotNull(inputStream)));
      final String[] encodedKeys = content.split("\n");
      final List<byte[]> keys = Lists.newArrayListWithCapacity(encodedKeys.length);
      for (String encodedKey : encodedKeys) {
        keys.add(decodeRowKey(encodedKey));
      }
      return keys;
    } finally {
      ResourceUtils.closeOrLog(inputStream);
    }
  }

@Test
  public void testDecodeSplitKeyFile() throws Exception {
    final String content =
        "key1\n"
        + "key2\n";
    final List<byte[]> keys =
        SplitKeyFile.decodeRegionSplitList(new ByteArrayInputStream(Bytes.toBytes(content)));
    assertEquals(2, keys.size());
    assertArrayEquals(Bytes.toBytes("key1"), keys.get(0));
    assertArrayEquals(Bytes.toBytes("key2"), keys.get(1));
  }


@Test
  public void testDecodeSplitKeyFileNoEndNewLine() throws Exception {
    final String content =
        "key1\n"
        + "key2";
    final List<byte[]> keys =
        SplitKeyFile.decodeRegionSplitList(new ByteArrayInputStream(Bytes.toBytes(content)));
    assertEquals(2, keys.size());
    assertArrayEquals(Bytes.toBytes("key1"), keys.get(0));
    assertArrayEquals(Bytes.toBytes("key2"), keys.get(1));
  }


Decodes a string encoded row key @param encoded Encoded row key @return the row key as a byte array @throws IOException on IO error     Escaped backslash   Escaped byte in hexadecimal

b'/**\n   * Decodes a string encoded row key.\n   *\n   * @param encoded Encoded row key.\n   * @return the row key, as a byte array.\n   * @throws IOException on I/O error.\n   */'public static byte[] decodeRowKey(String encoded) throws IOException {
    final ByteArrayOutputStream os = new ByteArrayOutputStream();
    int index = 0;
    final byte[] bytes = Bytes.toBytes(encoded);
    while (index < bytes.length) {
      final byte data = bytes[index++];

      if (data != '\\') {
        os.write(data);
      } else {
        if (index == bytes.length) {
          throw new IOException(String.format(
              "Invalid trailing escape in encoded row key: '%s'.", encoded));
        }
        final byte escaped = bytes[index++];

        switch (escaped) {
        case '\\': {
          // Escaped backslash:
          os.write('\\');
          break;
        }
        case 'x': {
          // Escaped byte in hexadecimal:
          if (index + 1 >= bytes.length) {
            throw new IOException(String.format(
                "Invalid hexadecimal escape in encoded row key: '%s'.", encoded));
          }
          final String hex = Bytes.toString(Arrays.copyOfRange(bytes, index, index + 2));
          try {
            final int decodedByte = Integer.parseInt(hex, 16);
            if ((decodedByte < 0) || (decodedByte > 255)) {
              throw new IOException(String.format(
                  "Invalid hexadecimal escape in encoded row key: '%s'.", encoded));
            }
            os.write(decodedByte);
          } catch (NumberFormatException nfe) {
            throw new IOException(String.format(
                "Invalid hexadecimal escape in encoded row key: '%s'.", encoded));
          }
          index += 2;
          break;
        }
        default:
          throw new IOException(String.format("Invalid escape in encoded row key: '%s'.", encoded));
        }
      }
   }
    return os.toByteArray();
  }

@Test
  public void testDecodeRowKey() throws Exception {
    assertArrayEquals(Bytes.toBytes("this is a \n key"),
        SplitKeyFile.decodeRowKey("this is a \n key"));

    assertArrayEquals(Bytes.toBytes("this is a \\ key"),
        SplitKeyFile.decodeRowKey("this is a \\\\ key"));

    assertArrayEquals(Bytes.toBytes("this is a \n key"),
        SplitKeyFile.decodeRowKey("this is a \\x0A key"));

    assertArrayEquals(Bytes.toBytes("this is a \n key"),
        SplitKeyFile.decodeRowKey("this is a \\x0a key"));
  }


('/**\\n   * Decodes a string encoded row key.\\n   *\\n   * @param encoded Encoded row key.\\n   * @return the row key, as a byte array.\\n   * @throws IOException on I/O error.\\n   */', '')

('', '// Escaped backslash:\n')

('', '// Escaped byte in hexadecimal:\n')

@Test
  public void testDecodeRowKeyInvalidHexEscape() throws Exception {
    try {
      SplitKeyFile.decodeRowKey("this is a \\xZZ key");
      fail("An exception should have been thrown.");
    } catch (IOException ioe) {
      assertEquals("Invalid hexadecimal escape in encoded row key: 'this is a \\xZZ key'.",
          ioe.getMessage());
    }
  }


('', '// Escaped backslash:\n')

@Test
  public void testDecodeRowKeyInvalidEscape() throws Exception {
    // \n is escaped as \x0a
    try {
      SplitKeyFile.decodeRowKey("this is a \\n key");
      fail("An exception should have been thrown.");
    } catch (IOException ioe) {
      assertEquals("Invalid escape in encoded row key: 'this is a \\n key'.", ioe.getMessage());
    }
  }


('', '// Escaped backslash:\n')

@Test
  public void testDecodeRowKeyUnterminatedEscape() throws Exception {
    try {
      SplitKeyFile.decodeRowKey("this is a \\");
      fail("An exception should have been thrown.");
    } catch (IOException ioe) {
      assertEquals("Invalid trailing escape in encoded row key: 'this is a \\'.", ioe.getMessage());
    }
  }


('', '// Escaped backslash:\n')

@Test
  public void testDecodeRowKeyInvalidHex() throws Exception {
    try {
      SplitKeyFile.decodeRowKey("this is a \\x-6");
      fail("An exception should have been thrown.");
    } catch (IOException ioe) {
      assertEquals("Invalid hexadecimal escape in encoded row key: 'this is a \\x-6'.",
          ioe.getMessage());
    }
  }


('', '// Escaped backslash:\n')

@Test
  public void testDecodeRowKeyIncompleteHex() throws Exception {
    try {
      SplitKeyFile.decodeRowKey("this is a \\x6");
      fail("An exception should have been thrown.");
    } catch (IOException ioe) {
      assertEquals("Invalid hexadecimal escape in encoded row key: 'this is a \\x6'.",
          ioe.getMessage());
    }
  }


['(\'/**\\\\n   * Gets a builder configured with default Kiji URI fields.\\\\n   *\\\\n   * More precisely, the following defaults are initialized:\\\\n   *
\\\\n   *
The Zookeeper quorum and client port is taken from the Hadoop <tt>Configuration</tt>
\\\\n   *
The Kiji instance name is set to <tt>KConstants.DEFAULT_INSTANCE_NAME</tt>\\\\n   *       (<tt>"default"</tt>).
\\\\n   *
The table name and column names are explicitly left unset.
\\\\n   *
\\\\n   *\\\\n   * @return A builder configured with this Kiji URI.\\\\n   */\', \'\')']

b'/**\n   * Gets a builder configured with default Kiji URI fields.\n   *\n   * More precisely, the following defaults are initialized:\n   *
\n   *
The Zookeeper quorum and client port is taken from the Hadoop <tt>Configuration</tt>
\n   *
The Kiji instance name is set to <tt>KConstants.DEFAULT_INSTANCE_NAME</tt>\n   *       (<tt>"default"</tt>).
\n   *
The table name and column names are explicitly left unset.
\n   *
\n   *\n   * @return A builder configured with this Kiji URI.\n   */'public static HBaseKijiURIBuilder newBuilder() {
    return new HBaseKijiURIBuilder();
  }

@Test
  public void testToString() {
    String uri = "kiji-hbase://(zkhost1,zkhost2):1234/instance/table/col/";
    assertEquals(uri, KijiURI.newBuilder(uri).build().toString());
    uri = "kiji-hbase://zkhost1:1234/instance/table/col/";
    assertEquals(uri, KijiURI.newBuilder(uri).build().toString());
    uri = "kiji-hbase://zkhost:1234/instance/table/col1,col2/";
    assertEquals(uri, KijiURI.newBuilder(uri).build().toString());
    uri = "kiji-hbase://zkhost:1234/.unset/table/col/";
    assertEquals(uri, KijiURI.newBuilder(uri).build().toString());
  }


Gets the version of the Kiji client software @return The version string @throws IOException on IO error     Proper release use the value of ImplementationVersion in METAINFMANIFESTMF   Most likely a development version

b'/**\n   * Gets the version of the Kiji client software.\n   *\n   * @return The version string.\n   * @throws IOException on I/O error.\n   */'public static String getSoftwareVersion() throws IOException {
    final String version = VersionInfo.class.getPackage().getImplementationVersion();
    if (version != null) {
      // Proper release: use the value of 'Implementation-Version' in META-INF/MANIFEST.MF:
      return version;
    }

    // Most likely a development version:
    final Properties kijiProps = loadKijiSchemaProperties();
    return kijiProps.getProperty(KIJI_SCHEMA_VERSION_PROP_NAME, DEFAULT_DEVELOPMENT_VERSION);
  }

@Test
  public void testGetSoftwareVersion() throws Exception {
    // We cannot compare against any concrete value here, or else the test will fail
    // depending on whether this is a development version or a release...
    assertFalse(VersionInfo.getSoftwareVersion().isEmpty());
  }


Reports the maximum system version of the Kiji instance format understood by the client p The instance format describes the layout of the global metadata state of a Kiji instance This version number specifies which Kiji instances it would be compatible with See @link isKijiVersionCompatible to determine whether a deployment is compatible with this version p @return A parsed version of the instance format protocol version string

b'/**\n   * Reports the maximum system version of the Kiji instance format understood by the client.\n   *\n   * <p>\n   *   The instance format describes the layout of the global metadata state of\n   *   a Kiji instance. This version number specifies which Kiji instances it would\n   *   be compatible with. See {@link #isKijiVersionCompatible} to determine whether\n   *   a deployment is compatible with this version.\n   * </p>\n   *\n   * @return A parsed version of the instance format protocol version string.\n   */'public static ProtocolVersion getClientDataVersion() {
    return Versions.MAX_SYSTEM_VERSION;
  }

@Test
  public void testGetClientDataVersion() {
    // This is the actual version we expect to be in there right now.
    assertEquals(Versions.MAX_SYSTEM_VERSION, VersionInfo.getClientDataVersion());
  }


Gets the version of the Kiji instance format installed on the HBase cluster pThe instance format describes the layout of the global metadata state of a Kiji instancep @param systemTable An open KijiSystemTable @return A parsed version of the storage format protocol version string @throws IOException on IO error

b'/**\n   * Gets the version of the Kiji instance format installed on the HBase cluster.\n   *\n   *
The instance format describes the layout of the global metadata state of\n   * a Kiji instance.
\n   *\n   * @param systemTable An open KijiSystemTable.\n   * @return A parsed version of the storage format protocol version string.\n   * @throws IOException on I/O error.\n   */'private static ProtocolVersion getClusterDataVersion(KijiSystemTable systemTable) throws
      IOException {
    try {
      final ProtocolVersion dataVersion = systemTable.getDataVersion();
      return dataVersion;
    } catch (TableNotFoundException e) {
      final KijiURI kijiURI = systemTable.getKijiURI();
      throw new KijiNotInstalledException(
          String.format("Kiji instance %s is not installed.", kijiURI),
          kijiURI);
    }
  }

@Test
  public void testGetClusterDataVersion() throws Exception {
    final KijiSystemTable systemTable = createMock(KijiSystemTable.class);
    // This version number for this test was picked out of a hat.
    expect(systemTable.getDataVersion()).andReturn(ProtocolVersion.parse("system-1.1")).anyTimes();

    final Kiji kiji = createMock(Kiji.class);
    expect(kiji.getSystemTable()).andReturn(systemTable).anyTimes();

    replay(systemTable);
    replay(kiji);
    assertEquals(ProtocolVersion.parse("system-1.1"), VersionInfo.getClusterDataVersion(kiji));
    verify(systemTable);
    verify(kiji);
  }


Validates that the client instance format version is compatible with the instance format version installed on a Kiji instance Throws IncompatibleKijiVersionException if not pFor the definition of compatibility used in this method see @link isKijiVersionCompatiblep @param kiji An open kiji instance @throws IOException on IO error reading the data version from the cluster or throws IncompatibleKijiVersionException if the installed instance format version is incompatible with the version supported by the client

b'/**\n   * Validates that the client instance format version is compatible with the instance\n   * format version installed on a Kiji instance.\n   * Throws IncompatibleKijiVersionException if not.\n   *\n   *
For the definition of compatibility used in this method, see {@link\n   * #isKijiVersionCompatible}
\n   *\n   * @param kiji An open kiji instance.\n   * @throws IOException on I/O error reading the data version from the cluster,\n   *     or throws IncompatibleKijiVersionException if the installed instance format version\n   *     is incompatible with the version supported by the client.\n   */'public static void validateVersion(Kiji kiji) throws IOException {
    validateVersion(kiji.getSystemTable());
  }

@Test
  public void testValidateVersion() throws Exception {
    final KijiSystemTable systemTable = createMock(KijiSystemTable.class);
    expect(systemTable.getDataVersion()).andReturn(VersionInfo.getClientDataVersion()).anyTimes();

    final Kiji kiji = createMock(Kiji.class);
    expect(kiji.getSystemTable()).andReturn(systemTable).anyTimes();

    replay(systemTable);
    replay(kiji);

    // This should validate, since we set the cluster data version to match the client version.
    VersionInfo.validateVersion(kiji);

    verify(systemTable);
    verify(kiji);
  }


('/**\\n   * Validates that the client instance format version is compatible with the instance\\n   * format version installed on a Kiji instance.\\n   * Throws IncompatibleKijiVersionException if not.\\n   *\\n   *
For the definition of compatibility used in this method, see {@link\\n   * #isKijiVersionCompatible}
\\n   *\\n   * @param kiji An open kiji instance.\\n   * @throws IOException on I/O error reading the data version from the cluster,\\n   *     or throws IncompatibleKijiVersionException if the installed instance format version\\n   *     is incompatible with the version supported by the client.\\n   */', '')

@Test
  public void testValidateVersionFail() throws Exception {
    final KijiSystemTable systemTable = createMock(KijiSystemTable.class);
    expect(systemTable.getDataVersion()).andReturn(ProtocolVersion.parse("kiji-0.9")).anyTimes();

    final Kiji kiji = createMock(Kiji.class);
    expect(kiji.getSystemTable()).andReturn(systemTable).anyTimes();

    replay(systemTable);
    replay(kiji);

    // This should throw an invalid version exception.
    try {
      VersionInfo.validateVersion(kiji);
      fail("An exception should have been thrown.");
    } catch (IncompatibleKijiVersionException ikve) {
      assertTrue(ikve.getMessage(), Pattern.matches(
          "Data format of Kiji instance \\(kiji-0\\.9\\) cannot operate "
          + "with client \\(system-[0-9]\\.[0-9]\\)",
          ikve.getMessage()));
    }
  }


['("/**\\\\n   * Actual comparison logic that validates client/cluster data compatibility according to\\\\n   * the rules defined in {@link #isKijiVersionCompatible(Kiji)}.\\\\n   *\\\\n   *
package-level visibility for unit testing.
\\\\n   *\\\\n   * @param clientVersion the client software\'s instance version.\\\\n   * @param clusterVersion the cluster\'s installed instance version.\\\\n   * @return true if the installed instance format\\\\n   *     version is compatible with this client, false otherwise.\\\\n   */", \'\')', '(\'\', \'// The "kiji-1.0" version is equivalent to "system-1.0" in compatibility tests.\\n\')', "('', '// See SCHEMA-469.\\n')", "('', '// From https://github.com/kijiproject/wiki/wiki/KijiVersionCompatibility:\\n')", "('', '//\\n')", "('', '// (quote)\\n')", "('', '//   Our primary goal with data formats is backwards compatibility. KijiSchema Version 1.x\\n')", "('', '//   will read and interact with data written by Version 1.y, if x > y.\\n')", "('', '//\\n')", "('', '// ...\\n')", "('', '//\\n')", "('', '//   The instance format describes the feature set and format of the entire Kiji instance.\\n')", "('', '//   This allows a client to determine whether it can connect to the instance at all, read\\n')", "('', '//   any cells, and identifies where other metadata (e.g., schemas, layouts) is located, as\\n')", "('', '//   well as the range of supported associated formats within that metadata.\\n')", "('', '//\\n')", "('', '//   The kiji version command specifies the instance version presently installed on the\\n')", "('', '//   cluster, and the system data version supported by the client. The major version numbers\\n')", "('', '//   of these must agree to connect to one another.\\n')", "('', '// (end quote)\\n')", "('', '//\\n')", '(\'\', "// Given these requirements, we require that our client\'s supported instance version major\\n")', "('', '// digit be greater than or equal to the major digit of the system installation.\\n')", "('', '//\\n')", "('', '// If we ever deprecate an instance format and then target it for end-of-life (i.e., when\\n')", "('', '// contemplating KijiSchema 2.0.0), we may introduce a cut-off point: we may support only\\n')", '(\'\', "// the most recent \'k\' major digits; so KijiSchema 2.0.0 may have system-12.0, but support\\n")', "('', '// reading/writing instances only as far back as system-9.0. This is not yet implemented;\\n')", "('', '// if we do go down this route, then this method would be the place to do it.\\n')"]

b"/**\n   * Actual comparison logic that validates client/cluster data compatibility according to\n   * the rules defined in {@link #isKijiVersionCompatible(Kiji)}.\n   *\n   *
package-level visibility for unit testing.
\n   *\n   * @param clientVersion the client software's instance version.\n   * @param clusterVersion the cluster's installed instance version.\n   * @return true if the installed instance format\n   *     version is compatible with this client, false otherwise.\n   */"static boolean areInstanceVersionsCompatible(
      ProtocolVersion clientVersion, ProtocolVersion clusterVersion) {

    if (Objects.equal(clusterVersion, Versions.SYSTEM_KIJI_1_0_DEPRECATED)) {
      // The "kiji-1.0" version is equivalent to "system-1.0" in compatibility tests.
      clusterVersion = Versions.SYSTEM_1_0;
    }

    // See SCHEMA-469.
    // From https://github.com/kijiproject/wiki/wiki/KijiVersionCompatibility:
    //
    // (quote)
    //   Our primary goal with data formats is backwards compatibility. KijiSchema Version 1.x
    //   will read and interact with data written by Version 1.y, if x > y.
    //
    // ...
    //
    //   The instance format describes the feature set and format of the entire Kiji instance.
    //   This allows a client to determine whether it can connect to the instance at all, read
    //   any cells, and identifies where other metadata (e.g., schemas, layouts) is located, as
    //   well as the range of supported associated formats within that metadata.
    //
    //   The kiji version command specifies the instance version presently installed on the
    //   cluster, and the system data version supported by the client. The major version numbers
    //   of these must agree to connect to one another.
    // (end quote)
    //
    // Given these requirements, we require that our client's supported instance version major
    // digit be greater than or equal to the major digit of the system installation.
    //
    // If we ever deprecate an instance format and then target it for end-of-life (i.e., when
    // contemplating KijiSchema 2.0.0), we may introduce a cut-off point: we may support only
    // the most recent 'k' major digits; so KijiSchema 2.0.0 may have system-12.0, but support
    // reading/writing instances only as far back as system-9.0. This is not yet implemented;
    // if we do go down this route, then this method would be the place to do it.

    return clientVersion.getProtocolName().equals(clusterVersion.getProtocolName())
        && clientVersion.getMajorVersion() >= clusterVersion.getMajorVersion();
  }

@Test
  public void testKijiVsSystemProtocol() {
    // New client (1.0.0-rc4 or higher) interacting with a table installed via 1.0.0-rc3.
    final ProtocolVersion clientVersion = ProtocolVersion.parse("system-1.0");
    final ProtocolVersion clusterVersion = ProtocolVersion.parse("kiji-1.0");

    assertTrue(VersionInfo.areInstanceVersionsCompatible(clientVersion, clusterVersion));
  }


@Test
  public void testOldKijiRefusesToReadNewSystemProtocol() {
    // The old 1.0.0-rc3 client should refuse to interop with a new instance created by
    // 1.0.0-rc4 or higher due to the data version protocol namespace change. Forwards
    // compatibility is not supported by the release candidates, even though we honor
    // backwards compatibility.
    final ProtocolVersion clientVersion = ProtocolVersion.parse("kiji-1.0");
    final ProtocolVersion clusterVersion = ProtocolVersion.parse("system-1.0");

    assertFalse(VersionInfo.areInstanceVersionsCompatible(clientVersion, clusterVersion));
  }


@Test
  public void testKijiVsNewerSystemProtocol() {
    // An even newer client (not yet defined as of 1.0.0-rc4) that uses a 'system-1.1'
    // protocol should still be compatible with a table installed via 1.0.0-rc3.
    // kiji-1.0 => system-1.0, and all system-1.x versions should be compatible.
    final ProtocolVersion clientVersion = ProtocolVersion.parse("system-1.1");
    final ProtocolVersion clusterVersion = ProtocolVersion.parse("kiji-1.0");

    assertTrue(VersionInfo.areInstanceVersionsCompatible(clientVersion, clusterVersion));
  }


@Test
  public void testDifferentSystemProtocols() {
    // In the future, when we release it, system-1.1 should be compatible with system-1.0.
    final ProtocolVersion clientVersion = ProtocolVersion.parse("system-1.1");
    final ProtocolVersion clusterVersion = ProtocolVersion.parse("system-1.0");

    assertTrue(VersionInfo.areInstanceVersionsCompatible(clientVersion, clusterVersion));
  }


@Test
  public void testSystemProtocolName() {
    // A client (1.0.0-rc4 or higher) interacting with a table installed via 1.0.0-rc4.
    final ProtocolVersion clientVersion = ProtocolVersion.parse("system-1.0");
    final ProtocolVersion clusterVersion = ProtocolVersion.parse("system-1.0");

    assertTrue(VersionInfo.areInstanceVersionsCompatible(clientVersion, clusterVersion));
  }


@Test
  public void testNormalizedQuorum() {
    KijiURI uri =
        KijiURI.newBuilder("kiji-hbase://(zkhost1,zkhost2):1234/instance/table/col/").build();
    KijiURI reversedQuorumUri =
        KijiURI.newBuilder("kiji-hbase://(zkhost2,zkhost1):1234/instance/table/col/").build();
    assertEquals(uri.toString(), reversedQuorumUri.toString());
    assertEquals(uri.getZookeeperQuorum(), reversedQuorumUri.getZookeeperQuorum());
  }


@Test
  public void testNoForwardCompatibility() {
    // A system-1.0-capable client should not be able to read a system-2.0 installation.
    final ProtocolVersion clientVersion = ProtocolVersion.parse("system-1.0");
    final ProtocolVersion clusterVersion = ProtocolVersion.parse("system-2.0");

    assertFalse(VersionInfo.areInstanceVersionsCompatible(clientVersion, clusterVersion));
  }


@Test
  public void testBackCompatibilityThroughMajorVersions() {
    // A system-2.0-capable client should still be able to read a system-1.0 installation.
    final ProtocolVersion clientVersion = ProtocolVersion.parse("system-2.0");
    final ProtocolVersion clusterVersion = ProtocolVersion.parse("system-1.0");

    assertTrue(VersionInfo.areInstanceVersionsCompatible(clientVersion, clusterVersion));
  }


Constructs a ZooKeeper lock object @param zookeeper ZooKeeper client @param lockDir Path of the directory node to use for the lock     ZooKeeperClientretain should be the last line of the constructor

b'/**\n   * Constructs a ZooKeeper lock object.\n   *\n   * @param zookeeper ZooKeeper client.\n   * @param lockDir Path of the directory node to use for the lock.\n   */'public ZooKeeperLock(ZooKeeperClient zookeeper, File lockDir) {
    this.mConstructorStack = CLEANUP_LOG.isDebugEnabled() ? Debug.getStackTrace() : null;

    this.mZKClient = zookeeper;
    this.mLockDir = lockDir;
    this.mLockPathPrefix = new File(lockDir,  LOCK_NAME_PREFIX);
    // ZooKeeperClient.retain() should be the last line of the constructor.
    this.mZKClient.retain();
    DebugResourceTracker.get().registerResource(this);
  }

@Test
  public void testZooKeeperLock() throws Exception {
    final File lockDir = new File("/lock");
    final ZooKeeperClient zkClient = ZooKeeperClient.getZooKeeperClient(getZKAddress());
    try {
      final CyclicBarrier barrier = new CyclicBarrier(2);
      final ZooKeeperLock lock1 = new ZooKeeperLock(zkClient, lockDir);
      final ZooKeeperLock lock2 = new ZooKeeperLock(zkClient, lockDir);
      lock1.lock();

      final Thread thread = new Thread() {
        /** {@inheritDoc} */
        @Override
        public void run() {
          try {
            assertFalse(lock2.lock(0.1));
            barrier.await();
            lock2.lock();
            lock2.unlock();

            lock2.lock();
            lock2.unlock();
            lock2.close();
            barrier.await();
          } catch (Exception e) {
            LOG.warn("Exception caught in locking thread: {}", e.getMessage());
          }
        }
      };
      thread.start();

      barrier.await(5, TimeUnit.SECONDS); // Eventually fail

      lock1.unlock();
      lock1.close();

      barrier.await(5, TimeUnit.SECONDS); // Eventually fail
    } finally {
      zkClient.release();
    }
  }


Try to recursively delete a directory in ZooKeeper If another thread modifies the directory or any children of the directory the recursive delete will fail and this method will return @code false @param zkClient connection to ZooKeeper @param path to the node to remove @return whether the delete succeeded or failed @throws IOException on unrecoverable ZooKeeper error     Node was deleted out from under us we still have to try again because if this is   thrown any parents of the deleted node possibly still exist   Someone else created a node in the tree between the time we built the transaction and   committed it

b'/**\n   * Try to recursively delete a directory in ZooKeeper. If another thread modifies the directory\n   * or any children of the directory, the recursive delete will fail, and this method will return\n   * {@code false}.\n   *\n   * @param zkClient connection to ZooKeeper.\n   * @param path to the node to remove.\n   * @return whether the delete succeeded or failed.\n   * @throws IOException on unrecoverable ZooKeeper error.\n   */'public static boolean atomicRecursiveDelete(
      final CuratorFramework zkClient,
      final String path
  ) throws IOException {
    try {
      buildAtomicRecursiveDelete(zkClient, zkClient.inTransaction(), path).commit();
      return true;
    } catch (NoNodeException nne) {
      LOG.debug("NoNodeException while attempting an atomic recursive delete: {}.",
          nne.getMessage());
      // Node was deleted out from under us; we still have to try again because if this is
      // thrown any parents of the deleted node possibly still exist.
    } catch (NotEmptyException nee) {
      LOG.debug("NotEmptyException while attempting an atomic recursive delete: {}.",
          nee.getMessage());
      // Someone else created a node in the tree between the time we built the transaction and
      // committed it.
    } catch (Exception e) {
      wrapAndRethrow(e);
    }
    return false;
  }

@Test
  public void testAtomicRecursiveDelete() throws Exception {
    final String path = "/foo/bar/baz/faz/fooz";
    mZKClient.create().creatingParentsIfNeeded().forPath(path);
    ZooKeeperUtils.atomicRecursiveDelete(mZKClient, "/foo");
    Assert.assertNull(mZKClient.checkExists().forPath("/foo"));
  }


('/**\\n   * Try to recursively delete a directory in ZooKeeper. If another thread modifies the directory\\n   * or any children of the directory, the recursive delete will fail, and this method will return\\n   * {@code false}.\\n   *\\n   * @param zkClient connection to ZooKeeper.\\n   * @param path to the node to remove.\\n   * @return whether the delete succeeded or failed.\\n   * @throws IOException on unrecoverable ZooKeeper error.\\n   */', '')

('', '// Node was deleted out from under us; we still have to try again because if this is\n')

('', '// thrown any parents of the deleted node possibly still exist.\n')

('', '// Someone else created a node in the tree between the time we built the transaction and\n')

('', '// committed it.\n')

@Test
  public void testAtomicRecursiveDeleteWithPersistentENode() throws Exception {
    final String path = "/foo/bar/baz/faz/fooz";

    PersistentEphemeralNode node =
        new PersistentEphemeralNode(mZKClient, Mode.EPHEMERAL, path, new byte[0]);
    try {
      node.start();
      node.waitForInitialCreate(5, TimeUnit.SECONDS);
      Assert.assertTrue(ZooKeeperUtils.atomicRecursiveDelete(mZKClient, "/foo"));
      Thread.sleep(1000); // Give ephemeral node time to recreate itself
      Assert.assertNotNull(mZKClient.checkExists().forPath("/foo"));
    } finally {
      node.close();
    }
  }


('/**\\n   * Build a transaction to atomically delete a directory tree.  Package private for testing.\\n   *\\n   * @param zkClient connection to ZooKeeper.\\n   * @param tx recursive transaction being built up.\\n   * @param path current directory to delete.\\n   * @return a transaction to delete the directory tree.\\n   * @throws Exception on unrecoverable ZooKeeper error.\\n   */', '')

b'/**\n   * Build a transaction to atomically delete a directory tree.  Package private for testing.\n   *\n   * @param zkClient connection to ZooKeeper.\n   * @param tx recursive transaction being built up.\n   * @param path current directory to delete.\n   * @return a transaction to delete the directory tree.\n   * @throws Exception on unrecoverable ZooKeeper error.\n   */'static CuratorTransactionFinal buildAtomicRecursiveDelete(
      final CuratorFramework zkClient,
      final CuratorTransaction tx,
      final String path
  ) throws Exception {
    final List<String> children = zkClient.getChildren().forPath(path);

    for (String child : children) {
      buildAtomicRecursiveDelete(zkClient, tx, path + "/" + child);
    }
    return tx.delete().forPath(path).and();
  }

@Test
  public void testAtomicRecusiveDeleteWithConcurrentNodeAddition() throws Exception {
    final String path = "/foo/bar/baz/faz/fooz";
    mZKClient.create().creatingParentsIfNeeded().forPath(path);
    CuratorTransactionFinal tx =
        ZooKeeperUtils.buildAtomicRecursiveDelete(mZKClient, mZKClient.inTransaction(), "/foo");
    mZKClient.create().forPath("/foo/new-child");

    try {
      tx.commit();
    } catch (NotEmptyException nee) {
      // expected
    }
    Assert.assertNotNull(mZKClient.checkExists().forPath("/foo"));
  }


@Test
  public void testAtomicRecusiveDeleteWithConcurrentNodeDeletion() throws Exception {
    final String path = "/foo/bar/baz/faz/fooz";
    mZKClient.create().creatingParentsIfNeeded().forPath(path);
    CuratorTransactionFinal tx =
        ZooKeeperUtils.buildAtomicRecursiveDelete(mZKClient, mZKClient.inTransaction(), "/foo");
    mZKClient.delete().forPath(path);

    try {
      tx.commit(); // should throw
    } catch (NoNodeException nne) {
      // expected
    }
    Assert.assertNotNull(mZKClient.checkExists().forPath("/foo"));
  }


['(\'/**\\\\n   * Create a new ZooKeeper client for the provided ensemble. The returned client is already\\\\n   * started, but the caller is responsible for closing it. The returned client *may* share an\\\\n   * underlying connection, therefore this method is not suitable if closing the client must\\\\n   * deterministically close outstanding session-based ZooKeeper items such as ephemeral nodes\\\\n   * or watchers.\\\\n   *\\\\n   * @param zkEnsemble of the ZooKeeper ensemble to connect to. May not be null.  May contain a\\\\n   *         namespace in suffix form, e.g. {@code "host1:port1,host2:port2/my/namespace"}.\\\\n   * @return a ZooKeeper client using the new connection.\\\\n   */\', \'\')']

b'/**\n   * Create a new ZooKeeper client for the provided ensemble. The returned client is already\n   * started, but the caller is responsible for closing it. The returned client *may* share an\n   * underlying connection, therefore this method is not suitable if closing the client must\n   * deterministically close outstanding session-based ZooKeeper items such as ephemeral nodes\n   * or watchers.\n   *\n   * @param zkEnsemble of the ZooKeeper ensemble to connect to. May not be null.  May contain a\n   *         namespace in suffix form, e.g. {@code "host1:port1,host2:port2/my/namespace"}.\n   * @return a ZooKeeper client using the new connection.\n   */'public static CuratorFramework getZooKeeperClient(final String zkEnsemble) {
    String address = zkEnsemble;
    String namespace = null;

    int index = zkEnsemble.indexOf('/');

    if (index != -1) {
      address = zkEnsemble.substring(0, index);
      namespace = zkEnsemble.substring(index + 1);
    }

    return CachedCuratorFramework.create(ZK_CLIENT_CACHE, address, namespace);
  }

@Test
  public void testZooKeeperConnectionsAreCached() throws Exception {
    final CuratorFramework other = ZooKeeperUtils.getZooKeeperClient(getZKAddress());
    try {
        Assert.assertTrue(mZKClient.getZookeeperClient() == other.getZookeeperClient());
    } finally {
      other.close();
    }
  }


@Test
  public void testFakeURIsAreNamespaced() throws Exception {
    final String namespace = "testFakeURIsAreNamespaced";
    final KijiURI uri = KijiURI.newBuilder("kiji://.fake." + namespace).build();
    final CuratorFramework framework = ZooKeeperUtils.getZooKeeperClient(uri);
    try {
      Assert.assertEquals(namespace, framework.getNamespace());
    } finally {
      framework.close();
    }
  }


b'/**\n   * Gets a builder configured with default Kiji URI fields.\n   *\n   * More precisely, the following defaults are initialized:\n   *
\n   *
The Zookeeper quorum and client port is taken from the Hadoop <tt>Configuration</tt>
\n   *
The Kiji instance name is set to <tt>KConstants.DEFAULT_INSTANCE_NAME</tt>\n   *       (<tt>"default"</tt>).
\n   *
The table name and column names are explicitly left unset.
\n   *
\n   *\n   * @return A builder configured with this Kiji URI.\n   */'public static CassandraKijiURIBuilder newBuilder() {
    return new CassandraKijiURIBuilder();
  }

@Test
  public void testCassandraUriDoubleApersand() {
    final String uriString = "kiji-cassandra://zkhost:1234/sally@cinnamon@chost:5678";
    try {
      final CassandraKijiURI uri = CassandraKijiURI.newBuilder(uriString).build();
      fail("An exception should have been thrown.");
    } catch (KijiURIException kurie) {
      assertEquals(String.format(
              "Invalid Kiji URI: '%s' : Cannot have more than one '@' in URI authority",
              uriString),
          kurie.getMessage());
    }
  }


@Test
  public void testNormalizedColumns() {
    KijiURI uri =
        KijiURI.newBuilder("kiji-hbase://(zkhost1,zkhost2):1234/instance/table/col/").build();
    KijiURI reversedColumnURI =
        KijiURI.newBuilder("kiji-hbase://(zkhost2,zkhost1):1234/instance/table/col/").build();
    assertEquals(uri.toString(), reversedColumnURI.toString());
    assertEquals(uri.getColumns(), reversedColumnURI.getColumns());
  }


@Test
  public void testCassandraUriTooManyColons() {
    final String uriString = "kiji-cassandra://zkhost:1234/sally:cinnamon:foo@chost:5678";
    try {
      final CassandraKijiURI uri = CassandraKijiURI.newBuilder(uriString).build();
      fail("An exception should have been thrown.");
    } catch (KijiURIException kurie) {
      assertEquals(String.format(
              "Invalid Kiji URI: '%s' : Cannot have more than one ':' in URI user info",
              uriString),
          kurie.getMessage());
    }
  }


@Test
  public void testCassandraUriWithUsername() {
    final String uriString = "kiji-cassandra://zkhost:1234/sallycinnamon@chost:5678";
    try {
      final CassandraKijiURI uri = CassandraKijiURI.newBuilder(uriString).build();
      fail("An exception should have been thrown.");
    } catch (KijiURIException kurie) {
      assertEquals(String.format(
          "Invalid Kiji URI: '%s' : Cassandra Kiji URIs do not support a username without a "
              + "password.",
          uriString),
          kurie.getMessage());
    }
  }


@Test
  public void testNoAuthority() {
    try {
      CassandraKijiURI.newBuilder("kiji-cassandra:///");
      fail("An exception should have been thrown.");
    } catch (KijiURIException kurie) {
      assertEquals("Invalid Kiji URI: 'kiji-cassandra:///' : ZooKeeper ensemble missing.",
          kurie.getMessage());
    }
  }


@Test
  public void testBrokenCassandraPort() {
    try {
      CassandraKijiURI.newBuilder("kiji-cassandra://zkhost/chost:port/default").build();
      fail("An exception should have been thrown.");
    } catch (KijiURIException kurie) {
      assertEquals(
          "Invalid Kiji URI: 'kiji-cassandra://zkhost/chost:port/default' "
              + ": Can not parse port 'port'.",
          kurie.getMessage());
    }
  }


@Test
  public void testMultipleHostsNoParen() {
    try {
      CassandraKijiURI.newBuilder(
          "kiji-cassandra://zkhost1,zkhost2:1234/chost:5678/instance/table/col");
      fail("An exception should have been thrown.");
    } catch (KijiURIException kurie) {
      assertEquals(
          "Invalid Kiji URI: 'kiji-cassandra://zkhost1,zkhost2:1234/chost:5678/instance/table/col'"
              + " : Multiple ZooKeeper hosts must be parenthesized.", kurie.getMessage());
    }
  }


@Test
  public void testMultipleHostsMultiplePorts() {
    try {
      CassandraKijiURI.newBuilder(
          "kiji-cassandra://zkhost1:1234,zkhost2:2345/chost:5678/instance/table/col");
      fail("An exception should have been thrown.");
    } catch (KijiURIException kurie) {
      assertEquals(
          "Invalid Kiji URI: 'kiji-cassandra://zkhost1:1234,zkhost2:2345/chost:5678/"
              + "instance/table/col' : Invalid ZooKeeper ensemble cluster identifier.",
          kurie.getMessage());
    }
  }


@Test
  public void testExtraPath() {
    try {
      CassandraKijiURI.newBuilder(
          "kiji-cassandra://(zkhost1,zkhost2):1234/chost:5678/instance/table/col/extra");
      fail("An exception should have been thrown.");
    } catch (KijiURIException kurie) {
      assertEquals(
          "Invalid Kiji URI: 'kiji-cassandra://(zkhost1,zkhost2):1234/chost:5678/"
              + "instance/table/col/extra' : Too many path segments.",
          kurie.getMessage());
    }
  }


@Test
  public void testToString() {
    String uri = "kiji-cassandra://(zkhost1,zkhost2):1234/chost:5678/instance/table/col/";
    assertEquals(uri, CassandraKijiURI.newBuilder(uri).build().toString());
    uri = "kiji-cassandra://zkhost1:1234/chost:5678/instance/table/col/";
    assertEquals(uri, CassandraKijiURI.newBuilder(uri).build().toString());
    uri = "kiji-cassandra://zkhost:1234/chost:5678/instance/table/col1,col2/";
    assertEquals(uri, CassandraKijiURI.newBuilder(uri).build().toString());
    uri = "kiji-cassandra://zkhost:1234/chost:5678/.unset/table/col/";
    assertEquals(uri, CassandraKijiURI.newBuilder(uri).build().toString());
  }


@Test
  public void testNormalizedQuorum() {
    CassandraKijiURI uri = CassandraKijiURI.newBuilder(
        "kiji-cassandra://(zkhost1,zkhost2):1234/chost:5678/instance/table/col/").build();
    CassandraKijiURI reversedQuorumUri = CassandraKijiURI.newBuilder(
        "kiji-cassandra://(zkhost2,zkhost1):1234/chost:5678/instance/table/col/").build();
    assertEquals(uri.toString(), reversedQuorumUri.toString());
    assertEquals(uri.getZookeeperQuorum(), reversedQuorumUri.getZookeeperQuorum());
  }


@Test
  public void testNormalizedColumns() {
    CassandraKijiURI uri = CassandraKijiURI.newBuilder(
        "kiji-cassandra://(zkhost1,zkhost2):1234/chost:5678/instance/table/col/").build();
    CassandraKijiURI reversedColumnURI = CassandraKijiURI.newBuilder(
        "kiji-cassandra://(zkhost2,zkhost1):1234/chost:5678/instance/table/col/").build();
    assertEquals(uri.toString(), reversedColumnURI.toString());
    assertEquals(uri.getColumns(), reversedColumnURI.getColumns());
  }


@Test
  public void testKijiURIBuilderDefault() {
    KijiURI uri = KijiURI.newBuilder().build();
    assertTrue(!uri.getZookeeperQuorum().isEmpty());  // Test cannot be more specific.
    // Test cannot validate the value of uri.getZookeeperClientPort().
    assertEquals(KConstants.DEFAULT_INSTANCE_NAME, uri.getInstance());
    assertEquals(null, uri.getTable());
    assertTrue(uri.getColumns().isEmpty());
  }


@Test
  public void testOrderedCassandraNodes() {
    String revString =  "kiji-cassandra://zkhost:1234/(chost2,chost1):5678/instance/table/col/";
    String ordString =  "kiji-cassandra://zkhost:1234/(chost1,chost2):5678/instance/table/col/";
    CassandraKijiURI revUri = CassandraKijiURI.newBuilder(revString).build();

    // "toString" should ignore the user-defined ordering.
    assertEquals(ordString, revUri.toString());

    // "toOrderedString" should maintain the user-defined ordering.

  }


@Test
  public void testCassandraKijiURIBuilderDefault() {
    CassandraKijiURI uri = CassandraKijiURI.newBuilder().build();
    assertTrue(!uri.getZookeeperQuorum().isEmpty());  // Test cannot be more specific.
    // Test cannot validate the value of uri.getZookeeperClientPort().
    assertEquals(KConstants.DEFAULT_INSTANCE_NAME, uri.getInstance());
    assertEquals(null, uri.getTable());
    assertTrue(uri.getColumns().isEmpty());
  }


@Test
  public void testCassandraKijiURIBuilderFromInstance() {
    final CassandraKijiURI uri = CassandraKijiURI.newBuilder(
        "kiji-cassandra://zkhost:1234/chost:5678/.unset/table").build();
    CassandraKijiURI built = CassandraKijiURI.newBuilder(uri).build();
    assertEquals(uri, built);
  }


@Test
  public void testCassandraKijiURIBuilderWithInstance() {
    final CassandraKijiURI uri = CassandraKijiURI.newBuilder(
        "kiji-cassandra://zkhost:1234/chost:5678/instance1/table").build();
    assertEquals("instance1", uri.getInstance());
    final CassandraKijiURI modified =
        CassandraKijiURI.newBuilder(uri).withInstanceName("instance2").build();
    assertEquals("instance2", modified.getInstance());
    assertEquals("instance1", uri.getInstance());
  }


@Test
  public void testSetColumn() {
    CassandraKijiURI uri = CassandraKijiURI.newBuilder(
        "kiji-cassandra://zkhost/chost:5678/instance/table/").build();
    assertTrue(uri.getColumns().isEmpty());
    uri =
        CassandraKijiURI.newBuilder(uri).withColumnNames(Arrays.asList("testcol1", "testcol2"))
        .build();
    assertEquals(2, uri.getColumns().size());
  }


@Test
  public void testSetZookeeperQuorum() {
    final CassandraKijiURI uri = CassandraKijiURI.newBuilder(
        "kiji-cassandra://zkhost/chost:5678/instance/table/col").build();
    final CassandraKijiURI modified = CassandraKijiURI.newBuilder(uri)
        .withZookeeperQuorum(new String[] {"zkhost1", "zkhost2"}).build();
    assertEquals(2, modified.getZookeeperQuorum().size());
    assertEquals("zkhost1", modified.getZookeeperQuorum().get(0));
    assertEquals("zkhost2", modified.getZookeeperQuorum().get(1));
  }


@Test
  public void testTrailingUnset() {
    final CassandraKijiURI uri = CassandraKijiURI.newBuilder(
        "kiji-cassandra://zkhost/chost:5678/.unset/table/.unset").build();
    CassandraKijiURI result = CassandraKijiURI.newBuilder(uri).withTableName(".unset").build();
    assertEquals("kiji-cassandra://zkhost:2181/chost:5678/", result.toString());
  }


@Test
  public void testEscapedMapColumnQualifier() {
    final CassandraKijiURI uri = CassandraKijiURI.newBuilder(
        "kiji-cassandra://zkhost/chost:5678/instance/table/map:one%20two").build();
    assertEquals("map:one two", uri.getColumns().get(0).getName());
  }


@Test
  public void testConstructedUriIsEscaped() {
    // SCHEMA-6. Column qualifier must be URL-encoded in CassandraKijiURI.
    final CassandraKijiURI uri =
        CassandraKijiURI.newBuilder("kiji-cassandra://zkhost/chost:5678/instance/table/")
        .addColumnName(new KijiColumnName("map:one two")).build();
    assertEquals(
        "kiji-cassandra://zkhost:2181/chost:5678/instance/table/map:one%20two/", uri.toString());
  }


('/**\\n   * Create a new {@link KijiResult} backed by Cassandra.\\n   *\\n   * @param entityId EntityId of the row from which to read cells.\\n   * @param dataRequest KijiDataRequest defining the values to retrieve.\\n   * @param table The table being viewed.\\n   * @param layout The layout of the table.\\n   * @param columnTranslator A column name translator for the table.\\n   * @param decoderProvider A cell decoder provider for the table.\\n   * @param <T> The type of value in the {@code KijiCell} of this view.\\n   * @return an {@code KijiResult}.\\n   * @throws IOException On error while decoding cells.\\n   */', '')

b'/**\n   * Create a new {@link KijiResult} backed by Cassandra.\n   *\n   * @param entityId EntityId of the row from which to read cells.\n   * @param dataRequest KijiDataRequest defining the values to retrieve.\n   * @param table The table being viewed.\n   * @param layout The layout of the table.\n   * @param columnTranslator A column name translator for the table.\n   * @param decoderProvider A cell decoder provider for the table.\n   * @param <T> The type of value in the {@code KijiCell} of this view.\n   * @return an {@code KijiResult}.\n   * @throws IOException On error while decoding cells.\n   */'public static <T> KijiResult<T> create(
      final EntityId entityId,
      final KijiDataRequest dataRequest,
      final CassandraKijiTable table,
      final KijiTableLayout layout,
      final CassandraColumnNameTranslator columnTranslator,
      final CellDecoderProvider decoderProvider
  ) throws IOException {
    final KijiDataRequestBuilder unpagedRequestBuilder = KijiDataRequest.builder();
    final KijiDataRequestBuilder pagedRequestBuilder = KijiDataRequest.builder();
    unpagedRequestBuilder.withTimeRange(
        dataRequest.getMinTimestamp(),
        dataRequest.getMaxTimestamp());
    pagedRequestBuilder.withTimeRange(dataRequest.getMinTimestamp(), dataRequest.getMaxTimestamp());

    for (final Column columnRequest : dataRequest.getColumns()) {
      if (columnRequest.getFilter() != null) {
        throw new UnsupportedOperationException(
            String.format("Cassandra Kiji does not support filters on column requests: %s.",
                columnRequest));
      }
      if (columnRequest.isPagingEnabled()) {
        pagedRequestBuilder.newColumnsDef(columnRequest);
      } else {
        unpagedRequestBuilder.newColumnsDef(columnRequest);
      }
    }

    final CellDecoderProvider requestDecoderProvider =
        decoderProvider.getDecoderProviderForRequest(dataRequest);

    final KijiDataRequest unpagedRequest = unpagedRequestBuilder.build();
    final KijiDataRequest pagedRequest = pagedRequestBuilder.build();

    if (unpagedRequest.isEmpty() && pagedRequest.isEmpty()) {
      return new EmptyKijiResult<T>(entityId, dataRequest);
    }

    final MaterializedKijiResult<T> materializedKijiResult;
    if (!unpagedRequest.isEmpty()) {
      materializedKijiResult =
          createMaterialized(
              table.getURI(),
              entityId,
              unpagedRequest,
              layout,
              columnTranslator,
              requestDecoderProvider,
              table.getAdmin());
    } else {
      materializedKijiResult = null;
    }

    final CassandraPagedKijiResult<T> pagedKijiResult;
    if (!pagedRequest.isEmpty()) {
      pagedKijiResult =
          new CassandraPagedKijiResult<T>(
              entityId,
              pagedRequest,
              table,
              layout,
              columnTranslator,
              requestDecoderProvider);
    } else {
      pagedKijiResult = null;
    }

    if (unpagedRequest.isEmpty()) {
      return pagedKijiResult;
    } else if (pagedRequest.isEmpty()) {
      return materializedKijiResult;
    } else {
      return DefaultKijiResult.create(dataRequest, materializedKijiResult, pagedKijiResult);
    }
  }

@Test
  public void testGetFullyQualifiedColumn() throws Exception {
    for (KijiColumnName column : ImmutableList.of(PRIMITIVE_STRING, STRING_MAP_1)) {
      for (int pageSize : ImmutableList.of(0, 1, 2, 10)) {
        { // Single version | no timerange
          final KijiDataRequest request = KijiDataRequest
              .builder()
              .addColumns(
                  ColumnsDef.create()
                      .withPageSize(pageSize)
                      .add(column.getFamily(), column.getQualifier()))
              .build();

          testViewGet(request, Iterables.limit(ROW_DATA.get(column).entrySet(), 1));
        }

        { // Single version | timerange
          final KijiDataRequest request = KijiDataRequest
              .builder()
              .addColumns(
                  ColumnsDef.create()
                      .withPageSize(pageSize)
                      .add(column.getFamily(), column.getQualifier()))
              .withTimeRange(4, 6)
              .build();

          testViewGet(
              request,
              Iterables.limit(ROW_DATA.get(column).subMap(6L, false, 4L, true).entrySet(), 1));
        }

        { // Multiple versions | no timerange
          final KijiDataRequest request = KijiDataRequest
              .builder()
              .addColumns(
                  ColumnsDef.create()
                      .withPageSize(pageSize)
                      .withMaxVersions(100)
                      .add(column.getFamily(), column.getQualifier()))
              .build();

          testViewGet(
              request,
              ROW_DATA.get(column).entrySet());
        }

        { // Multiple versions | timerange
          final KijiDataRequest request = KijiDataRequest
              .builder()
              .addColumns(
                  ColumnsDef.create()
                      .withPageSize(pageSize)
                      .withMaxVersions(100)
                      .add(column.getFamily(), column.getQualifier()))
              .withTimeRange(4, 6)
              .build();

          testViewGet(
              request,
              ROW_DATA.get(column).subMap(6L, false, 4L, true).entrySet());
        }
      }
    }
  }


["('/** {@inheritDoc} */', '')", "('', '// In the case that we have enough information to generate a prefix, we will\\n')", '(\'\', "// construct a PrefixFilter that is AND\'ed with the RowFilter.  This way,\\n")', "('', '// when the scan passes the end of the prefix, it can end the filtering\\n')", "('', '// process quickly.\\n')", "('', '// Define a regular expression that effectively creates a mask for the row\\n')", "('', '// key based on the key format and the components passed in.  Prefix hashes\\n')", "('', '// are skipped over, and null components match any value.\\n')", "('', '// ISO-8859-1 is used so that numerals map to valid characters\\n')", "('', '// see http://stackoverflow.com/a/1387184\\n')", "('', '// Use the embedded flag (?s) to turn on single-line mode; this makes the\\n')", '(\'\', "// \'.\' match on line terminators.  The RegexStringComparator uses the DOTALL\\n")', "('', '// flag during construction, but it does not use that flag upon\\n')", "('', '// deserialization.  This bug has been fixed in HBASE-5667, part of the 0.95\\n')", "('', '// release.\\n')", "('', '// ^ matches the beginning of the string\\n')", "('', '// If all of the components included in the hash have been specified,\\n')", "('', '// then match on the value of the hash\\n')", "('', '// Write the hashed bytes to the prefix buffer\\n')", '(\'\', "// match any character exactly \'hash size\' number of times\\n")', "('', '// Only add to the prefix buffer until we hit a null component.  We do this\\n')", "('', '// here, because the prefix is going to expect to have 0x00 component\\n')", "('', '// terminators, which we put in during this loop but not in the loop above.\\n')", "('', '// null integers can be excluded entirely if there are just nulls at\\n')", "('', '// the end of the EntityId, otherwise, match the correct number of\\n')", "('', '// bytes\\n')", "('', '// match each byte in the integer using a regex hex sequence\\n')", "('', '// null longs can be excluded entirely if there are just nulls at\\n')", "('', '// the end of the EntityId, otherwise, match the correct number of\\n')", "('', '// bytes\\n')", "('', '// match each byte in the long using a regex hex sequence\\n')", "('', '// match any non-zero character at least once, followed by a zero\\n')", "('', '// delimiter, or match nothing at all in case the component was at\\n')", "('', '// the end of the EntityId and skipped entirely\\n')", "('', '// FormattedEntityId converts a string component to UTF-8 bytes to\\n')", "('', '// create the HBase key.  RegexStringComparator will convert the\\n')", "('', '// value it sees (ie, the HBase key) to a string using ISO-8859-1.\\n')", "('', '// Therefore, we need to write ISO-8859-1 characters to our regex so\\n')", "('', '// that the comparison happens correctly.\\n')", "('', '// $ matches the end of the string\\n')"]

b'/** {@inheritDoc} */'@Override
  public Filter toHBaseFilter(Context context) throws IOException {
    // In the case that we have enough information to generate a prefix, we will
    // construct a PrefixFilter that is AND'ed with the RowFilter.  This way,
    // when the scan passes the end of the prefix, it can end the filtering
    // process quickly.
    boolean addPrefixFilter = false;
    ByteArrayOutputStream prefixBytes = new ByteArrayOutputStream();

    // Define a regular expression that effectively creates a mask for the row
    // key based on the key format and the components passed in.  Prefix hashes
    // are skipped over, and null components match any value.
    // ISO-8859-1 is used so that numerals map to valid characters
    // see http://stackoverflow.com/a/1387184

    // Use the embedded flag (?s) to turn on single-line mode; this makes the
    // '.' match on line terminators.  The RegexStringComparator uses the DOTALL
    // flag during construction, but it does not use that flag upon
    // deserialization.  This bug has been fixed in HBASE-5667, part of the 0.95
    // release.
    StringBuilder regex = new StringBuilder("(?s)^"); // ^ matches the beginning of the string
    if (null != mRowKeyFormat.getSalt()) {
      if (mRowKeyFormat.getSalt().getHashSize() > 0) {
        // If all of the components included in the hash have been specified,
        // then match on the value of the hash
        Object[] prefixComponents =
            getNonNullPrefixComponents(mComponents, mRowKeyFormat.getRangeScanStartIndex());
        if (prefixComponents.length == mRowKeyFormat.getRangeScanStartIndex()) {
          ByteArrayOutputStream tohash = new ByteArrayOutputStream();
          for (Object component : prefixComponents) {
            byte[] componentBytes = toBytes(component);
            tohash.write(componentBytes, 0, componentBytes.length);
          }
          byte[] hashed = Arrays.copyOfRange(Hasher.hash(tohash.toByteArray()), 0,
              mRowKeyFormat.getSalt().getHashSize());
          for (byte hashedByte : hashed) {
            regex.append(String.format("\\x%02x", hashedByte & 0xFF));
          }

          addPrefixFilter = true;
          // Write the hashed bytes to the prefix buffer
          prefixBytes.write(hashed, 0, hashed.length);
        } else {
          // match any character exactly 'hash size' number of times
          regex.append(".{").append(mRowKeyFormat.getSalt().getHashSize()).append("}");
        }
      }
    }
    // Only add to the prefix buffer until we hit a null component.  We do this
    // here, because the prefix is going to expect to have 0x00 component
    // terminators, which we put in during this loop but not in the loop above.
    boolean hitNullComponent = false;
    for (int i = 0; i < mComponents.length; i++) {
      final Object component = mComponents[i];
      switch (mRowKeyFormat.getComponents().get(i).getType()) {
        case INTEGER:
          if (null == component) {
            // null integers can be excluded entirely if there are just nulls at
            // the end of the EntityId, otherwise, match the correct number of
            // bytes
            regex.append("(.{").append(Bytes.SIZEOF_INT).append("})?");
            hitNullComponent = true;
          } else {
            byte[] tempBytes = toBytes((Integer) component);
            // match each byte in the integer using a regex hex sequence
            for (byte tempByte : tempBytes) {
              regex.append(String.format("\\x%02x", tempByte & 0xFF));
            }
            if (!hitNullComponent) {
              prefixBytes.write(tempBytes, 0, tempBytes.length);
            }
          }
          break;
        case LONG:
          if (null == component) {
            // null longs can be excluded entirely if there are just nulls at
            // the end of the EntityId, otherwise, match the correct number of
            // bytes
            regex.append("(.{").append(Bytes.SIZEOF_LONG).append("})?");
            hitNullComponent = true;
          } else {
            byte[] tempBytes = toBytes((Long) component);
            // match each byte in the long using a regex hex sequence
            for (byte tempByte : tempBytes) {
              regex.append(String.format("\\x%02x", tempByte & 0xFF));
            }
            if (!hitNullComponent) {
              prefixBytes.write(tempBytes, 0, tempBytes.length);
            }
          }
          break;
        case STRING:
          if (null == component) {
            // match any non-zero character at least once, followed by a zero
            // delimiter, or match nothing at all in case the component was at
            // the end of the EntityId and skipped entirely
            regex.append("([^\\x00]+\\x00)?");
            hitNullComponent = true;
          } else {
            // FormattedEntityId converts a string component to UTF-8 bytes to
            // create the HBase key.  RegexStringComparator will convert the
            // value it sees (ie, the HBase key) to a string using ISO-8859-1.
            // Therefore, we need to write ISO-8859-1 characters to our regex so
            // that the comparison happens correctly.
            byte[] utfBytes = toBytes((String) component);
            String isoString = new String(utfBytes, Charsets.ISO_8859_1);
            regex.append(isoString).append("\\x00");
            if (!hitNullComponent) {
              prefixBytes.write(utfBytes, 0, utfBytes.length);
              prefixBytes.write((byte) 0);
            }
          }
          break;
        default:
          throw new IllegalStateException("Unknown component type: "
              + mRowKeyFormat.getComponents().get(i).getType());
      }
    }
    regex.append("$"); // $ matches the end of the string

    final RowFilter regexRowFilter =
        SchemaPlatformBridge.get().createRowFilterFromRegex(CompareOp.EQUAL, regex.toString());
    if (addPrefixFilter) {
      return new FilterList(new PrefixFilter(prefixBytes.toByteArray()), regexRowFilter);
    }
    return regexRowFilter;
  }

@Test
  public void testPrefixFilterHaltsFiltering() throws Exception {
    RowKeyFormat2 rowKeyFormat = createRowKeyFormat(1, INTEGER, LONG, LONG);
    EntityIdFactory factory = EntityIdFactory.getFactory(rowKeyFormat);
    FormattedEntityIdRowFilter filter = createFilter(rowKeyFormat, 100, null, 9000L);
    Filter hbaseFilter = filter.toHBaseFilter(null);

    EntityId passingEntityId = factory.getEntityId(100, 100L, 9000L);
    byte[] passingHbaseKey = passingEntityId.getHBaseRowKey();
    doInclusionAssert(rowKeyFormat, filter, passingEntityId, hbaseFilter, passingHbaseKey, INCLUDE);
    boolean filterAllRemaining = hbaseFilter.filterAllRemaining();
    String message = createFailureMessage(rowKeyFormat, filter, passingEntityId, hbaseFilter,
        passingHbaseKey, filterAllRemaining);
    assertEquals(message, false, filterAllRemaining);

    EntityId failingEntityId = factory.getEntityId(101, 100L, 9000L);
    byte[] failingHbaseKey = failingEntityId.getHBaseRowKey();
    doInclusionAssert(rowKeyFormat, filter, failingEntityId, hbaseFilter, failingHbaseKey, EXCLUDE);
    filterAllRemaining = hbaseFilter.filterAllRemaining();
    message = createFailureMessage(rowKeyFormat, filter, failingEntityId, hbaseFilter,
        failingHbaseKey, filterAllRemaining);
    assertEquals(message, true, filterAllRemaining);
  }


['(\'/**\\\\n   * Gets a builder configured with default Kiji URI fields.\\\\n   *\\\\n   * More precisely, the following defaults are initialized:\\\\n   *
\\\\n   *
The Zookeeper quorum and client port is taken from the Hadoop <tt>Configuration</tt>
\\\\n   *
The Kiji instance name is set to <tt>KConstants.DEFAULT_INSTANCE_NAME</tt>\\\\n   *       (<tt>"default"</tt>).
\\\\n   *
The table name and column names are explicitly left unset.
\\\\n   *
\\\\n   *\\\\n   * @return A builder configured with this Kiji URI.\\\\n   */\', \'\')']

@Test
  public void testKijiURIBuilderFromInstance() {
    final KijiURI uri = KijiURI.newBuilder("kiji-hbase://zkhost:1234/.unset/table").build();
    KijiURI built = KijiURI.newBuilder(uri).build();
    assertEquals(uri, built);
  }


@Test
  public void testGetMultipleFullyQualifiedColumns() throws Exception {
    final KijiColumnName column1 = PRIMITIVE_STRING;
    final KijiColumnName column2 = STRING_MAP_1;

    for (int pageSize : ImmutableList.of(0, 1, 2, 10)) {

      { // Single version | no timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create()
                    .withPageSize(pageSize)
                    .add(column1.getFamily(), column1.getQualifier())
                    .add(column2.getFamily(), column2.getQualifier()))
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            Iterables.limit(ROW_DATA.get(column1).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            Iterables.limit(ROW_DATA.get(column2).entrySet(), 1);

        testViewGet(request, Iterables.concat(column1Entries, column2Entries));
      }

      { // Single version | timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create()
                    .withPageSize(pageSize)
                    .add(column1.getFamily(), column1.getQualifier())
                    .add(column2.getFamily(), column2.getQualifier()))
            .withTimeRange(4, 6)
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            Iterables.limit(ROW_DATA.get(column1).subMap(6L, false, 4L, true).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            Iterables.limit(ROW_DATA.get(column2).subMap(6L, false, 4L, true).entrySet(), 1);

        testViewGet(request, Iterables.concat(column1Entries, column2Entries));
      }

      { // Multiple versions | no timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create()
                    .withPageSize(pageSize)
                    .withMaxVersions(100)
                    .add(column1.getFamily(), column1.getQualifier())
                    .add(column2.getFamily(), column2.getQualifier()))
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries = ROW_DATA.get(column1).entrySet();
        final Iterable<? extends Entry<Long, ?>> column2Entries = ROW_DATA.get(column2).entrySet();

        testViewGet(request, Iterables.concat(column1Entries, column2Entries));
      }

      { // Multiple versions | timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create()
                    .withPageSize(pageSize)
                    .withMaxVersions(100)
                    .add(column1.getFamily(), column1.getQualifier())
                    .add(column2.getFamily(), column2.getQualifier()))
            .withTimeRange(4, 6)
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            ROW_DATA.get(column1).subMap(6L, false, 4L, true).entrySet();
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            ROW_DATA.get(column2).subMap(6L, false, 4L, true).entrySet();

        testViewGet(request, Iterables.concat(column1Entries, column2Entries));
      }

      { // Mixed versions | timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create()
                    .withPageSize(pageSize)
                    .withMaxVersions(100)
                    .add(column1.getFamily(), column1.getQualifier()))
            .addColumns(
                ColumnsDef.create()
                    .withPageSize(pageSize)
                    .withMaxVersions(1)
                    .add(column2.getFamily(), column2.getQualifier()))
            .withTimeRange(4, 6)
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            ROW_DATA.get(column1).subMap(6L, false, 4L, true).entrySet();
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            Iterables.limit(ROW_DATA.get(column2).subMap(6L, false, 4L, true).entrySet(), 1);

        testViewGet(request, Iterables.concat(column1Entries, column2Entries));
      }

      { // Mixed versions | no timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create()
                    .withPageSize(pageSize)
                    .withMaxVersions(1)
                    .add(column1.getFamily(), column1.getQualifier()))
            .addColumns(
                ColumnsDef.create()
                    .withPageSize(pageSize)
                    .withMaxVersions(100)
                    .add(column2.getFamily(), column2.getQualifier()))
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            Iterables.limit(ROW_DATA.get(column1).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            ROW_DATA.get(column2).entrySet();

        testViewGet(request, Iterables.concat(column1Entries, column2Entries));
      }
    }
  }


@Test
  public void testGetFamilyColumn() throws Exception {
    final Map<String, ? extends List<KijiColumnName>> families =
        ImmutableMap.of(
            PRIMITIVE_FAMILY, ImmutableList.of(PRIMITIVE_DOUBLE, PRIMITIVE_STRING),
            STRING_MAP_FAMILY, ImmutableList.of(STRING_MAP_1, STRING_MAP_2));

    for (Entry<String, ? extends List<KijiColumnName>> family : families.entrySet()) {
      for (int pageSize : ImmutableList.of(0, 1, 2, 10)) {

        final KijiColumnName familyColumn = KijiColumnName.create(family.getKey(), null);
        final KijiColumnName column1 = family.getValue().get(0);
        final KijiColumnName column2 = family.getValue().get(1);

        { // Single version | no timerange
          final KijiDataRequest request = KijiDataRequest
              .builder()
              .addColumns(ColumnsDef.create().withPageSize(pageSize).add(familyColumn))
              .build();

          final Iterable<? extends Entry<Long, ?>> column1Entries =
              Iterables.limit(ROW_DATA.get(column1).entrySet(), 1);
          final Iterable<? extends Entry<Long, ?>> column2Entries =
              Iterables.limit(ROW_DATA.get(column2).entrySet(), 1);

          testViewGet(request, Iterables.concat(column1Entries, column2Entries));
        }

        { // Single version | timerange
          final KijiDataRequest request = KijiDataRequest
              .builder()
              .addColumns(ColumnsDef.create().withPageSize(pageSize).add(familyColumn))
              .withTimeRange(4, 6)
              .build();

          final Iterable<? extends Entry<Long, ?>> column1Entries =
              Iterables.limit(ROW_DATA.get(column1).subMap(6L, false, 4L, true).entrySet(), 1);
          final Iterable<? extends Entry<Long, ?>> column2Entries =
              Iterables.limit(ROW_DATA.get(column2).subMap(6L, false, 4L, true).entrySet(), 1);

          testViewGet(request, Iterables.concat(column1Entries, column2Entries));
        }

        { // Multiple versions | no timerange
          final KijiDataRequest request = KijiDataRequest
              .builder()
              .addColumns(
                  ColumnsDef
                      .create()
                      .withPageSize(pageSize)
                      .withMaxVersions(100)
                      .add(familyColumn))
              .build();

          final Iterable<? extends Entry<Long, ?>> column1Entries =
              ROW_DATA.get(column1).entrySet();
          final Iterable<? extends Entry<Long, ?>> column2Entries =
              ROW_DATA.get(column2).entrySet();

          testViewGet(request, Iterables.concat(column1Entries, column2Entries));
        }

        { // Multiple versions | timerange
          final KijiDataRequest request = KijiDataRequest
              .builder()
              .addColumns(
                  ColumnsDef
                      .create()
                      .withPageSize(pageSize)
                      .withMaxVersions(100)
                      .add(familyColumn))
              .withTimeRange(4, 6)
              .build();

          final Iterable<? extends Entry<Long, ?>> column1Entries =
              ROW_DATA.get(column1).subMap(6L, false, 4L, true).entrySet();
          final Iterable<? extends Entry<Long, ?>> column2Entries =
              ROW_DATA.get(column2).subMap(6L, false, 4L, true).entrySet();

          testViewGet(request, Iterables.concat(column1Entries, column2Entries));
        }
      }
    }
  }


@Test
  public void testGetMultipleFamilyColumns() throws Exception {
    final KijiColumnName familyColumn1 = KijiColumnName.create(PRIMITIVE_FAMILY, null);
    final KijiColumnName familyColumn2 = KijiColumnName.create(STRING_MAP_FAMILY, null);

    final KijiColumnName column1 = PRIMITIVE_DOUBLE;
    final KijiColumnName column2 = PRIMITIVE_STRING;
    final KijiColumnName column3 = STRING_MAP_1;
    final KijiColumnName column4 = STRING_MAP_2;

    for (int pageSize : ImmutableList.of(0, 1, 2, 10)) {

      { // Single version | no timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(ColumnsDef.create().add(familyColumn1))
            .addColumns(ColumnsDef.create().add(familyColumn2))
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            Iterables.limit(ROW_DATA.get(column1).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            Iterables.limit(ROW_DATA.get(column2).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column3Entries =
            Iterables.limit(ROW_DATA.get(column3).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column4Entries =
            Iterables.limit(ROW_DATA.get(column4).entrySet(), 1);

        testViewGet(
            request,
            Iterables.concat(column1Entries, column2Entries, column3Entries, column4Entries));
      }

      { // Single version | timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(ColumnsDef.create().withPageSize(pageSize).add(familyColumn1))
            .addColumns(ColumnsDef.create().withPageSize(pageSize).add(familyColumn2))
            .withTimeRange(4, 6)
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            Iterables.limit(ROW_DATA.get(column1).subMap(6L, false, 4L, true).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            Iterables.limit(ROW_DATA.get(column2).subMap(6L, false, 4L, true).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column3Entries =
            Iterables.limit(ROW_DATA.get(column3).subMap(6L, false, 4L, true).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column4Entries =
            Iterables.limit(ROW_DATA.get(column4).subMap(6L, false, 4L, true).entrySet(), 1);

        testViewGet(
            request,
            Iterables.concat(column1Entries, column2Entries, column3Entries, column4Entries));
      }

      { // Multiple versions | no timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create().withPageSize(pageSize).withMaxVersions(100).add(familyColumn1))
            .addColumns(
                ColumnsDef.create().withPageSize(pageSize).withMaxVersions(100).add(familyColumn2))
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries = ROW_DATA.get(column1).entrySet();
        final Iterable<? extends Entry<Long, ?>> column2Entries = ROW_DATA.get(column2).entrySet();
        final Iterable<? extends Entry<Long, ?>> column3Entries = ROW_DATA.get(column3).entrySet();
        final Iterable<? extends Entry<Long, ?>> column4Entries = ROW_DATA.get(column4).entrySet();

        testViewGet(
            request,
            Iterables.concat(column1Entries, column2Entries, column3Entries, column4Entries));
      }

      { // Multiple versions | timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create().withPageSize(pageSize).withMaxVersions(100).add(familyColumn1))
            .addColumns(
                ColumnsDef.create().withPageSize(pageSize).withMaxVersions(100).add(familyColumn2))
            .withTimeRange(4, 6)
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            ROW_DATA.get(column1).subMap(6L, false, 4L, true).entrySet();
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            ROW_DATA.get(column2).subMap(6L, false, 4L, true).entrySet();
        final Iterable<? extends Entry<Long, ?>> column3Entries =
            ROW_DATA.get(column3).subMap(6L, false, 4L, true).entrySet();
        final Iterable<? extends Entry<Long, ?>> column4Entries =
            ROW_DATA.get(column4).subMap(6L, false, 4L, true).entrySet();

        testViewGet(
            request,
            Iterables.concat(column1Entries, column2Entries, column3Entries, column4Entries));
      }

      { // Mixed versions | no timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create().withPageSize(pageSize).withMaxVersions(2).add(familyColumn1))
            .addColumns(
                ColumnsDef.create().withPageSize(pageSize).withMaxVersions(100).add(familyColumn2))
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            Iterables.limit(ROW_DATA.get(column1).entrySet(), 2);
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            Iterables.limit(ROW_DATA.get(column2).entrySet(), 2);
        final Iterable<? extends Entry<Long, ?>> column3Entries = ROW_DATA.get(column3).entrySet();
        final Iterable<? extends Entry<Long, ?>> column4Entries = ROW_DATA.get(column4).entrySet();

        testViewGet(
            request,
            Iterables.concat(column1Entries, column2Entries, column3Entries, column4Entries));
      }

      { // Multiple versions | timerange
        final KijiDataRequest request = KijiDataRequest
            .builder()
            .addColumns(
                ColumnsDef.create().withPageSize(pageSize).withMaxVersions(100).add(familyColumn1))
            .addColumns(
                ColumnsDef.create().withPageSize(pageSize).withMaxVersions(1).add(familyColumn2))
            .withTimeRange(4, 6)
            .build();

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            ROW_DATA.get(column1).subMap(6L, false, 4L, true).entrySet();
        final Iterable<? extends Entry<Long, ?>> column2Entries =
            ROW_DATA.get(column2).subMap(6L, false, 4L, true).entrySet();
        final Iterable<? extends Entry<Long, ?>> column3Entries =
            Iterables.limit(ROW_DATA.get(column3).subMap(6L, false, 4L, true).entrySet(), 1);
        final Iterable<? extends Entry<Long, ?>> column4Entries =
            Iterables.limit(ROW_DATA.get(column4).subMap(6L, false, 4L, true).entrySet(), 1);

        testViewGet(
            request,
            Iterables.concat(column1Entries, column2Entries, column3Entries, column4Entries));
      }
    }
  }


@Test
  public void testNarrowView() throws Exception {
    final KijiColumnName familyColumn1 = KijiColumnName.create(PRIMITIVE_FAMILY, null);
    final KijiColumnName familyColumn2 = KijiColumnName.create(STRING_MAP_FAMILY, null);

    final KijiColumnName column1 = PRIMITIVE_DOUBLE;
    final KijiColumnName column2 = PRIMITIVE_STRING;
    final KijiColumnName column3 = STRING_MAP_1;
    final KijiColumnName column4 = STRING_MAP_2;

    for (int pageSize : ImmutableList.of(0)) {

      final KijiDataRequest request = KijiDataRequest
          .builder()
          .addColumns(
              ColumnsDef.create().withPageSize(pageSize).withMaxVersions(100).add(familyColumn1))
          .addColumns(
              ColumnsDef.create().withPageSize(pageSize).withMaxVersions(100).add(column3))
          .addColumns(ColumnsDef.create().withPageSize(pageSize).add(column4))
          .withTimeRange(2, 10)
          .build();

      final KijiResult<Object> view = mReader.getResult(mTable.getEntityId(ROW), request);
      try {
        testViewGet(view.narrowView(PRIMITIVE_LONG), ImmutableList.<Entry<Long, ?>>of());

        final Iterable<? extends Entry<Long, ?>> column1Entries =
            ROW_DATA.get(column1).subMap(10L, false, 2L, true).entrySet();
        testViewGet(view.narrowView(column1), column1Entries);

        final Iterable<? extends Entry<Long, ?>> column2Entries =
            ROW_DATA.get(column2).subMap(10L, false, 2L, true).entrySet();
        testViewGet(view.narrowView(column2), column2Entries);

        testViewGet(view.narrowView(familyColumn1),
            Iterables.concat(column1Entries, column2Entries));

        final Iterable<? extends Entry<Long, ?>> column3Entries =
            ROW_DATA.get(column3).subMap(10L, false, 2L, true).entrySet();
        testViewGet(view.narrowView(column3), column3Entries);

        final Iterable<? extends Entry<Long, ?>> column4Entries =
            Iterables.limit(ROW_DATA.get(column4).subMap(10L, false, 2L, true).entrySet(), 1);
        testViewGet(view.narrowView(column4), column4Entries);

        testViewGet(view.narrowView(familyColumn2),
            Iterables.concat(column3Entries, column4Entries));
      } finally {
        view.close();
      }
    }
  }


@Test(expected = UnsupportedOperationException.class)
  public void testGetWithFilters() throws Exception {
    final KijiColumnName column2 = STRING_MAP_1;

    final KijiDataRequest request = KijiDataRequest
        .builder()
        .addColumns(
            ColumnsDef.create()
                .withFilter(
                    new KijiColumnRangeFilter(
                        STRING_MAP_1.getQualifier(), true,
                        STRING_MAP_2.getQualifier(), false))
                .withMaxVersions(10)
                .add(column2.getFamily(), null))
        .build();

    final Iterable<? extends Entry<Long, ?>> column1Entries = ROW_DATA.get(column2).entrySet();

    testViewGet(request, column1Entries);
  }