killbill-aplcache

usage: initial import of ScalarSample ScalarSample maps

7/28/2012 4:08:22 PM

Details

diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/samples/NullSample.java b/usage/src/main/java/com/ning/billing/usage/timeline/samples/NullSample.java
new file mode 100644
index 0000000..efa99fa
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/samples/NullSample.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.usage.timeline.samples;
+
+public class NullSample extends ScalarSample<Void> {
+
+    public NullSample() {
+        super(SampleOpcode.NULL, null);
+    }
+}
diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/samples/RepeatSample.java b/usage/src/main/java/com/ning/billing/usage/timeline/samples/RepeatSample.java
new file mode 100644
index 0000000..990ef64
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/samples/RepeatSample.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.usage.timeline.samples;
+
+/**
+ * A repeated value
+ *
+ * @param <T> A value consistent with the opcode
+ */
+public class RepeatSample<T> extends SampleBase {
+
+    public static final int MAX_BYTE_REPEAT_COUNT = 0xFF; // The maximum byte value
+    public static final int MAX_SHORT_REPEAT_COUNT = 0xFFFF; // The maximum short value
+
+    private final ScalarSample<T> sampleRepeated;
+
+    private int repeatCount;
+
+    public RepeatSample(final int repeatCount, final ScalarSample<T> sampleRepeated) {
+        super(SampleOpcode.REPEAT_BYTE);
+        this.repeatCount = repeatCount;
+        this.sampleRepeated = sampleRepeated;
+    }
+
+    public int getRepeatCount() {
+        return repeatCount;
+    }
+
+    public void incrementRepeatCount() {
+        repeatCount++;
+    }
+
+    public void incrementRepeatCount(final int addend) {
+        repeatCount += addend;
+    }
+
+    public ScalarSample<T> getSampleRepeated() {
+        return sampleRepeated;
+    }
+
+    @Override
+    public SampleOpcode getOpcode() {
+        return repeatCount > MAX_BYTE_REPEAT_COUNT ? SampleOpcode.REPEAT_SHORT : SampleOpcode.REPEAT_BYTE;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("RepeatSample");
+        sb.append("{sampleRepeated=").append(sampleRepeated);
+        sb.append(", repeatCount=").append(repeatCount);
+        sb.append('}');
+        return sb.toString();
+    }
+
+    @Override
+    public boolean equals(final Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+
+        final RepeatSample that = (RepeatSample) o;
+
+        if (repeatCount != that.repeatCount) {
+            return false;
+        }
+        if (sampleRepeated != null ? !sampleRepeated.equals(that.sampleRepeated) : that.sampleRepeated != null) {
+            return false;
+        }
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = sampleRepeated != null ? sampleRepeated.hashCode() : 0;
+        result = 31 * result + repeatCount;
+        return result;
+    }
+}
diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/samples/SampleBase.java b/usage/src/main/java/com/ning/billing/usage/timeline/samples/SampleBase.java
new file mode 100644
index 0000000..25072e5
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/samples/SampleBase.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.usage.timeline.samples;
+
+public abstract class SampleBase {
+
+    protected final SampleOpcode opcode;
+
+    public SampleBase(final SampleOpcode opcode) {
+        this.opcode = opcode;
+    }
+
+    public SampleOpcode getOpcode() {
+        return opcode;
+    }
+}
diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/samples/ScalarSample.java b/usage/src/main/java/com/ning/billing/usage/timeline/samples/ScalarSample.java
new file mode 100644
index 0000000..a4f3173
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/samples/ScalarSample.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you 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.ning.billing.usage.timeline.samples;
+
+import java.lang.reflect.InvocationTargetException;
+import java.math.BigInteger;
+import java.util.Map;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.primitives.Ints;
+import com.google.common.primitives.Shorts;
+
+/**
+ * A sample value associated with its opcode
+ *
+ * @param <T> A value consistent with the opcode
+ */
+public class ScalarSample<T> extends SampleBase {
+
+    private static final String KEY_OPCODE = "O";
+    private static final String KEY_SAMPLE_CLASS = "K";
+    private static final String KEY_SAMPLE_VALUE = "V";
+
+    private final T sampleValue;
+
+    public static ScalarSample fromObject(final Object sampleValue) {
+        if (sampleValue == null) {
+            return new ScalarSample<Void>(SampleOpcode.NULL, null);
+        } else if (sampleValue instanceof Byte) {
+            return new ScalarSample<Byte>(SampleOpcode.BYTE, (Byte) sampleValue);
+        } else if (sampleValue instanceof Short) {
+            return new ScalarSample<Short>(SampleOpcode.SHORT, (Short) sampleValue);
+        } else if (sampleValue instanceof Integer) {
+            try {
+                // Can it fit in a short?
+                final short optimizedShort = Shorts.checkedCast(Long.valueOf(sampleValue.toString()));
+                return new ScalarSample<Short>(SampleOpcode.SHORT, optimizedShort);
+            } catch (IllegalArgumentException e) {
+                return new ScalarSample<Integer>(SampleOpcode.INT, (Integer) sampleValue);
+            }
+        } else if (sampleValue instanceof Long) {
+            try {
+                // Can it fit in a short?
+                final short optimizedShort = Shorts.checkedCast(Long.valueOf(sampleValue.toString()));
+                return new ScalarSample<Short>(SampleOpcode.SHORT, optimizedShort);
+            } catch (IllegalArgumentException e) {
+                try {
+                    // Can it fit in an int?
+                    final int optimizedLong = Ints.checkedCast(Long.valueOf(sampleValue.toString()));
+                    return new ScalarSample<Integer>(SampleOpcode.INT, optimizedLong);
+                } catch (IllegalArgumentException ohWell) {
+                    return new ScalarSample<Long>(SampleOpcode.LONG, (Long) sampleValue);
+                }
+            }
+        } else if (sampleValue instanceof Float) {
+            return new ScalarSample<Float>(SampleOpcode.FLOAT, (Float) sampleValue);
+        } else if (sampleValue instanceof Double) {
+            return new ScalarSample<Double>(SampleOpcode.DOUBLE, (Double) sampleValue);
+        } else {
+            return new ScalarSample<String>(SampleOpcode.STRING, sampleValue.toString());
+        }
+    }
+
+    public ScalarSample(final SampleOpcode opcode, final T sampleValue) {
+        super(opcode);
+        this.sampleValue = sampleValue;
+    }
+
+    public ScalarSample(final String opcode, final T sampleValue) {
+        this(SampleOpcode.valueOf(opcode), sampleValue);
+    }
+
+    public double getDoubleValue() {
+        final Object sampleValue = getSampleValue();
+        return getDoubleValue(getOpcode(), sampleValue);
+    }
+
+    public static double getDoubleValue(final SampleOpcode opcode, final Object sampleValue) {
+        switch (opcode) {
+            case NULL:
+            case DOUBLE_ZERO:
+            case INT_ZERO:
+                return 0.0;
+            case BYTE:
+            case BYTE_FOR_DOUBLE:
+                return (double) ((Byte) sampleValue);
+            case SHORT:
+            case SHORT_FOR_DOUBLE:
+                return (double) ((Short) sampleValue);
+            case INT:
+                return (double) ((Integer) sampleValue);
+            case LONG:
+                return (double) ((Long) sampleValue);
+            case FLOAT:
+            case FLOAT_FOR_DOUBLE:
+                return (double) ((Float) sampleValue);
+            case HALF_FLOAT_FOR_DOUBLE:
+                return (double) HalfFloat.toFloat((Short) sampleValue);
+            case DOUBLE:
+                return (Double) sampleValue;
+            case BIGINT:
+                return ((BigInteger) sampleValue).doubleValue();
+            default:
+                throw new IllegalArgumentException(String.format("In getDoubleValue(), sample opcode is %s, sample value is %s",
+                                                                 opcode.name(), String.valueOf(sampleValue)));
+        }
+    }
+
+    @JsonCreator
+    public ScalarSample(@JsonProperty(KEY_OPCODE) final byte opcodeIdx,
+                        @JsonProperty(KEY_SAMPLE_CLASS) final Class klass,
+                        @JsonProperty(KEY_SAMPLE_VALUE) final T sampleValue) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
+        super(SampleOpcode.getOpcodeFromIndex(opcodeIdx));
+        // Numerical classes have a String constructor
+        this.sampleValue = (T) klass.getConstructor(String.class).newInstance(sampleValue.toString());
+    }
+
+    @JsonValue
+    public Map<String, Object> toMap() {
+        // Work around type erasure by storing explicitly the sample class. This avoid deserializing shorts as integers
+        // at replay time for instance
+        return ImmutableMap.of(KEY_OPCODE, opcode.getOpcodeIndex(), KEY_SAMPLE_CLASS, sampleValue.getClass(), KEY_SAMPLE_VALUE, sampleValue);
+    }
+
+    public T getSampleValue() {
+        return sampleValue;
+    }
+
+    @Override
+    public String toString() {
+        return sampleValue.toString();
+    }
+
+    @Override
+    public boolean equals(final Object other) {
+        if (other == null || !(other instanceof SampleBase)) {
+            return false;
+        }
+        final ScalarSample otherSample = (ScalarSample) other;
+        final Object otherValue = otherSample.getSampleValue();
+        if (getOpcode() != otherSample.getOpcode()) {
+            return false;
+        } else if (!opcode.getNoArgs() && !(sameSampleValues(sampleValue, otherValue))) {
+            return false;
+        }
+        return true;
+    }
+
+    public static boolean sameSampleValues(final Object o1, final Object o2) {
+        if (o1 == o2) {
+            return true;
+        } else if (o1.getClass() == o2.getClass()) {
+            return o1.equals(o2);
+        } else {
+            return false;
+        }
+    }
+
+    @Override
+    public int hashCode() {
+        return sampleValue != null ? sampleValue.hashCode() : 0;
+    }
+}
diff --git a/usage/src/test/java/com/ning/billing/usage/timeline/samples/TestNullSample.java b/usage/src/test/java/com/ning/billing/usage/timeline/samples/TestNullSample.java
new file mode 100644
index 0000000..369bba4
--- /dev/null
+++ b/usage/src/test/java/com/ning/billing/usage/timeline/samples/TestNullSample.java
@@ -0,0 +1,15 @@
+package com.ning.billing.usage.timeline.samples;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestNullSample {
+
+    @Test(groups = "fast")
+    public void testConstructor() throws Exception {
+        final NullSample sample = new NullSample();
+
+        Assert.assertEquals(sample.getOpcode(), SampleOpcode.NULL);
+        Assert.assertNull(sample.getSampleValue());
+    }
+}
diff --git a/usage/src/test/java/com/ning/billing/usage/timeline/samples/TestRepeatSample.java b/usage/src/test/java/com/ning/billing/usage/timeline/samples/TestRepeatSample.java
new file mode 100644
index 0000000..1cd647c
--- /dev/null
+++ b/usage/src/test/java/com/ning/billing/usage/timeline/samples/TestRepeatSample.java
@@ -0,0 +1,33 @@
+package com.ning.billing.usage.timeline.samples;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestRepeatSample {
+
+    @Test(groups = "fast")
+    public void testGetters() throws Exception {
+        final int repeatCount = 5;
+        final ScalarSample<Short> scalarSample = new ScalarSample<Short>(SampleOpcode.SHORT, (short) 12);
+        final RepeatSample<Short> repeatSample = new RepeatSample<Short>(repeatCount, scalarSample);
+
+        Assert.assertEquals(repeatSample.getRepeatCount(), repeatCount);
+        Assert.assertEquals(repeatSample.getSampleRepeated(), scalarSample);
+        Assert.assertEquals(repeatSample.getOpcode(), scalarSample.getOpcode());
+    }
+
+    @Test(groups = "fast")
+    public void testEquals() throws Exception {
+        final int repeatCount = 5;
+        final ScalarSample<Short> scalarSample = new ScalarSample<Short>(SampleOpcode.SHORT, (short) 12);
+
+        final RepeatSample<Short> repeatSample = new RepeatSample<Short>(repeatCount, scalarSample);
+        Assert.assertEquals(repeatSample, repeatSample);
+
+        final RepeatSample<Short> sameRepeatSample = new RepeatSample<Short>(repeatCount, scalarSample);
+        Assert.assertEquals(sameRepeatSample, repeatSample);
+
+        final RepeatSample<Short> otherRepeatSample = new RepeatSample<Short>(repeatCount + 1, scalarSample);
+        Assert.assertNotEquals(otherRepeatSample, repeatSample);
+    }
+}
diff --git a/usage/src/test/java/com/ning/billing/usage/timeline/samples/TestScalarSample.java b/usage/src/test/java/com/ning/billing/usage/timeline/samples/TestScalarSample.java
new file mode 100644
index 0000000..98626a3
--- /dev/null
+++ b/usage/src/test/java/com/ning/billing/usage/timeline/samples/TestScalarSample.java
@@ -0,0 +1,59 @@
+package com.ning.billing.usage.timeline.samples;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+public class TestScalarSample {
+
+    @Test(groups = "fast")
+    public void testGetters() throws Exception {
+        final SampleOpcode opcode = SampleOpcode.SHORT;
+        final short value = (short) 5;
+        final ScalarSample<Short> scalarSample = new ScalarSample<Short>(opcode, value);
+
+        Assert.assertEquals(scalarSample.getOpcode(), opcode);
+        Assert.assertEquals((short) scalarSample.getSampleValue(), value);
+    }
+
+    @Test(groups = "fast")
+    public void testEquals() throws Exception {
+        final SampleOpcode opcode = SampleOpcode.SHORT;
+        final short value = (short) 5;
+
+        final ScalarSample<Short> scalarSample = new ScalarSample<Short>(opcode, value);
+        Assert.assertEquals(scalarSample, scalarSample);
+
+        final ScalarSample<Short> sameScalarSample = new ScalarSample<Short>(opcode, value);
+        Assert.assertEquals(sameScalarSample, scalarSample);
+
+        final ScalarSample<Short> otherScalarSample = new ScalarSample<Short>(opcode, (short) (value + 1));
+        Assert.assertNotEquals(otherScalarSample, scalarSample);
+    }
+
+    @Test(groups = "fast")
+    public void testFromObject() throws Exception {
+        verifyFromObject(null, 0.0, null, SampleOpcode.NULL);
+
+        verifyFromObject((byte) 1, (double) 1, 1, SampleOpcode.BYTE);
+
+        verifyFromObject((short) 128, (double) 128, (short) 128, SampleOpcode.SHORT);
+        verifyFromObject(32767, (double) 32767, (short) 32767, SampleOpcode.SHORT);
+
+        verifyFromObject(32768, (double) 32768, 32768, SampleOpcode.INT);
+        verifyFromObject((long) 32767, (double) 32767, (short) 32767, SampleOpcode.SHORT);
+        verifyFromObject((long) 32768, (double) 32768, 32768, SampleOpcode.INT);
+
+        verifyFromObject(2147483648L, (double) 2147483648L, 2147483648L, SampleOpcode.LONG);
+
+        verifyFromObject((float) 1.3, 1.3, (float) 1.3, SampleOpcode.FLOAT);
+
+        verifyFromObject(12.24, 12.24, 12.24, SampleOpcode.DOUBLE);
+    }
+
+    private void verifyFromObject(final Object value, final double expectedDoubleValue, final Object expectedSampleValue, final SampleOpcode expectedSampleOpcode) {
+        final ScalarSample scalarSample = ScalarSample.fromObject(value);
+        Assert.assertEquals(scalarSample.getOpcode(), expectedSampleOpcode);
+        Assert.assertEquals(scalarSample.getSampleValue(), expectedSampleValue);
+        Assert.assertEquals(scalarSample.getDoubleValue(), expectedDoubleValue);
+    }
+}