/** * Copyright 2005-2012 Akiban Technologies, Inc. * * 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.persistit.unit; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import java.io.Serializable; import java.net.MalformedURLException; import java.net.URL; import junit.framework.Assert; import org.junit.Test; import com.persistit.Key; import com.persistit.PersistitUnitTestCase; import com.persistit.TestShim; import com.persistit.encoding.CoderContext; import com.persistit.encoding.KeyCoder; import com.persistit.encoding.KeyDisplayer; import com.persistit.encoding.KeyRenderer; import com.persistit.exception.ConversionException; import com.persistit.exception.PersistitException; import com.persistit.util.Util; public class KeyCoderTest1 extends PersistitUnitTestCase { @Test public void normalKeyCoder() throws MalformedURLException { final KeyCoder coder = new TestKeyRenderer1(); _persistit.getCoderManager().registerKeyCoder(URL.class, coder); final Key key = new Key(_persistit); final URL url1 = new URL("http://w/z"); final URL url2 = new URL("http://w:8080/z?userid=pb"); key.clear(); key.append("a"); key.append(url1); key.append("b"); key.append(url2); key.append("c"); key.reset(); final String a = key.decodeString(); final Object obj1 = key.decode(); final String b = key.decodeString(); final Object obj2 = key.decode(); final String c = key.decodeString(); assertEquals("a", a); assertEquals("b", b); assertEquals("c", c); assertEquals(url1.toString(), obj1.toString()); assertEquals(url2.toString(), obj2.toString()); final StringBuilder sb = new StringBuilder(); key.indexTo(1); key.decode(sb); assertEquals(url1.toString(), sb.toString()); sb.setLength(0); key.indexTo(3); key.decode(sb); assertEquals(url2.toString(), sb.toString()); key.reset(); final String toString = key.toString(); assertEquals(toString, "{\"a\",(java.net.URL){\"http\",\"w\",-1,\"/z\"},\"b\"," + "(java.net.URL){\"http\",\"w\",8080,\"/z?userid=pb\"},\"c\"}"); } @Test public void zeroByteFreeKeyCoder() throws Exception { final KeyCoder coder = new TestKeyRenderer2(); _persistit.getCoderManager().registerKeyCoder(WrappedString.class, coder); final Key key = new Key(_persistit); key.append(1.2f); key.append(new WrappedString(RED_FOX)); key.append("another key"); key.append(new WrappedString("abcde")); key.reset(); assertEquals(Float.valueOf(1.2f), key.decode()); final Object ws = key.decode(); assertTrue(ws instanceof WrappedString); assertEquals(RED_FOX, ((WrappedString) ws)._value); assertEquals("another key", key.decodeString()); key.decode(ws); assertEquals("abcde", ((WrappedString) ws)._value); } @Test public void normalKeyCoderQuotesNulls() throws Exception { final KeyCoder coder = new TestKeyRenderer3(); _persistit.getCoderManager().registerKeyCoder(WrappedLong.class, coder); final Key key = new Key(_persistit); for (long value = 0; value < 32; value++) { key.append(new WrappedLong(value)); } key.reset(); for (int depth = 0; depth < 50; depth++) { key.indexTo(depth); if (key.getIndex() == key.getEncodedSize()) { assertEquals(32, depth); break; } final long value = ((WrappedLong) key.decode())._value; assertEquals(depth, value); } } @Test(expected = ConversionException.class) public void zeroByteFreeKeyCoderThrows() throws Exception { final KeyCoder coder = new TestKeyRenderer2(); _persistit.getCoderManager().registerKeyCoder(WrappedString.class, coder); final Key key = new Key(_persistit); final WrappedString ws = new WrappedString(new String(new byte[] { 'a', 'b', 0, 'c' })); key.append(ws); } @Test public void keyHandleEncodeDecode() throws Exception { final int[] handles = { 64, 0x1FE, 0x1FF, 0x200, 0x201, 0x7FFE, 0x7FFF, 0x8000, 0x8001, Integer.MAX_VALUE }; final KeyCoder coder = new TestKeyRenderer1(); final Key key1 = new Key(_persistit); final URL url1 = new URL("http://w/z"); final URL url2 = new URL("http://w:8080/z?userid=pb"); for (final int handle : handles) { registerCoderDefinedKeyHandle(coder, handle); key1.clear(); key1.append(url1); key1.append(url2); key1.reset(); final Object obj1 = key1.decode(); final Object obj2 = key1.decode(); assertEquals(url1.toString(), obj1.toString()); assertEquals(url2.toString(), obj2.toString()); final StringBuilder sb = new StringBuilder(); key1.indexTo(1); key1.decode(sb); assertEquals(url2.toString(), sb.toString()); } } private void registerCoderDefinedKeyHandle(final KeyCoder coder, final int handle) throws PersistitException { TestShim.clearAllClassIndexEntries(_persistit); TestShim.setClassIndexTestIdFloor(_persistit, handle); _persistit.getCoderManager().registerKeyCoder(URL.class, coder); } public static void main(final String[] args) throws Exception { new KeyCoderTest1().initAndRunTest(); } public void debugAssert(final boolean condition) { Assert.assertTrue(condition); } public static class TestKeyRenderer1 implements KeyRenderer { @Override public void appendKeySegment(final Key key, final Object object, final CoderContext context) { final URL url = (URL) object; key.append(url.getProtocol()); key.append(url.getHost()); key.append(url.getPort()); key.append(url.getFile()); } @Override public Object decodeKeySegment(final Key key, final Class<?> cl, final CoderContext context) { final String protocol = key.decodeString(); final String host = key.decodeString(); final int port = key.decodeInt(); final String file = key.decodeString(); try { return new URL(protocol, host, port, file); } catch (final MalformedURLException mue) { throw new ConversionException(mue); } } @Override public void renderKeySegment(final Key key, final Object target, final Class<?> cl, final CoderContext context) { final StringBuilder sb = (StringBuilder) target; key.decodeString(sb); sb.append("://"); key.decodeString(sb); final int port = key.decodeInt(); if (port != -1) { sb.append(':'); sb.append(port); } key.decodeString(sb); } @Override public boolean isZeroByteFree() { return false; } } @SuppressWarnings("serial") public static class WrappedString implements Serializable { String _value; WrappedString(final String value) { _value = value; } } @SuppressWarnings("serial") public static class WrappedLong implements Serializable { long _value; WrappedLong(final long value) { _value = value; } } public static class TestKeyRenderer2 implements KeyRenderer { @Override public void appendKeySegment(final Key key, final Object object, final CoderContext context) { final WrappedString cstring = (WrappedString) object; final byte[] bytes = cstring._value.getBytes(); final int size = key.getEncodedSize(); System.arraycopy(bytes, 0, key.getEncodedBytes(), size, bytes.length); key.setEncodedSize(size + bytes.length); } @Override public Object decodeKeySegment(final Key key, final Class<?> cl, final CoderContext context) { final WrappedString target = new WrappedString(null); renderKeySegment(key, target, cl, context); return target; } @Override public void renderKeySegment(final Key key, final Object target, final Class<?> cl, final CoderContext context) { final WrappedString cstring = (WrappedString) target; final int size = key.getEncodedSize(); final int index = key.getIndex(); final byte[] b = new byte[size - index]; System.arraycopy(key.getEncodedBytes(), index, b, 0, b.length); cstring._value = new String(b); } @Override public boolean isZeroByteFree() { return true; } } public static class TestKeyRenderer3 implements KeyRenderer, KeyDisplayer { @Override public void appendKeySegment(final Key key, final Object object, final CoderContext context) { final WrappedLong wlong = (WrappedLong) object; final int size = key.getEncodedSize(); Util.putLong(key.getEncodedBytes(), size, wlong._value); key.setEncodedSize(size + 8); } @Override public Object decodeKeySegment(final Key key, final Class<?> cl, final CoderContext context) { final WrappedLong target = new WrappedLong(Long.MIN_VALUE); renderKeySegment(key, target, cl, context); return target; } @Override public void renderKeySegment(final Key key, final Object target, final Class<?> cl, final CoderContext context) { final WrappedLong wl = (WrappedLong) target; final int size = key.getEncodedSize(); final int index = key.getIndex(); assertEquals(8, size - index); wl._value = Util.getLong(key.getEncodedBytes(), index); key.setIndex(index + 8); } @Override public boolean isZeroByteFree() { return false; } @Override public void displayKeySegment(final Key key, final Appendable target, final Class<?> clazz, final CoderContext context) throws ConversionException { final WrappedLong wl = new WrappedLong(Long.MIN_VALUE); renderKeySegment(key, wl, clazz, context); Util.append(target, Long.toString(wl._value)); } } }