cassandra-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From slebre...@apache.org
Subject svn commit: r1101120 - in /cassandra/branches/cassandra-0.8.1: ./ src/java/org/apache/cassandra/db/marshal/ test/unit/org/apache/cassandra/ test/unit/org/apache/cassandra/db/marshal/
Date Mon, 09 May 2011 17:24:34 GMT
Author: slebresne
Date: Mon May  9 17:24:34 2011
New Revision: 1101120

URL: http://svn.apache.org/viewvc?rev=1101120&view=rev
Log:
Add CompositeType and DynamicCompositeType
patch by slebresne; reviewed by edanuff for CASSANDRA-2231

Added:
    cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java
    cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java
    cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java
    cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
    cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java
Modified:
    cassandra/branches/cassandra-0.8.1/CHANGES.txt
    cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java
    cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java

Modified: cassandra/branches/cassandra-0.8.1/CHANGES.txt
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/CHANGES.txt?rev=1101120&r1=1101119&r2=1101120&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8.1/CHANGES.txt (original)
+++ cassandra/branches/cassandra-0.8.1/CHANGES.txt Mon May  9 17:24:34 2011
@@ -6,6 +6,7 @@
 0.8.1
  * add support for comparator parameters and a generic ReverseType
    (CASSANDRA-2355)
+ * add CompositeType and DynamicCompositeType (CASSANDRA-2231)
 
 0.8.0-?
  * faster flushes and compaction from fixing excessively pessimistic 

Added: cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java?rev=1101120&view=auto
==============================================================================
--- cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java (added)
+++ cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/AbstractCompositeType.java Mon May  9 17:24:34 2011
@@ -0,0 +1,294 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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 org.apache.cassandra.db.marshal;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.sql.Types;
+
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+/**
+ * A class avoiding class duplication between CompositeType and
+ * DynamicCompositeType.
+ * Those two differs only in that for DynamicCompositeType, the comparators
+ * are in the encoded column name at the front of each component.
+ */
+public abstract class AbstractCompositeType extends AbstractType<ByteBuffer>
+{
+    // changes bb position
+    protected static int getShortLength(ByteBuffer bb)
+    {
+        int length = (bb.get() & 0xFF) << 8;
+        return length | (bb.get() & 0xFF);
+    }
+
+    // changes bb position
+    protected static void putShortLength(ByteBuffer bb, int length)
+    {
+        bb.put((byte) ((length >> 8) & 0xFF));
+        bb.put((byte) (length & 0xFF));
+    }
+
+    // changes bb position
+    protected static ByteBuffer getBytes(ByteBuffer bb, int length)
+    {
+        ByteBuffer copy = bb.duplicate();
+        copy.limit(copy.position() + length);
+        bb.position(bb.position() + length);
+        return copy;
+    }
+
+    // changes bb position
+    protected static ByteBuffer getWithShortLength(ByteBuffer bb)
+    {
+        int length = getShortLength(bb);
+        return getBytes(bb, length);
+    }
+
+    public int compare(ByteBuffer o1, ByteBuffer o2)
+    {
+        if (null == o1)
+            return null == o2 ? 0 : -1;
+
+        ByteBuffer bb1 = o1.duplicate();
+        ByteBuffer bb2 = o2.duplicate();
+        int i = 0;
+
+        while (bb1.remaining() > 0 && bb2.remaining() > 0)
+        {
+            AbstractType comparator = getNextComparator(i, bb1, bb2);
+
+            ByteBuffer value1 = getWithShortLength(bb1);
+            ByteBuffer value2 = getWithShortLength(bb2);
+
+            int cmp = comparator.compare(value1, value2);
+            if (cmp != 0)
+                return cmp;
+
+            byte b1 = bb1.get();
+            byte b2 = bb2.get();
+            if (b1 < 0)
+            {
+                if (b2 >= 0)
+                    return -1;
+            }
+            else if (b1 > 0)
+            {
+                if (b2 <= 0)
+                    return 1;
+            }
+            else
+            {
+                // b1 == 0
+                if (b2 != 0)
+                    return -b2;
+            }
+            ++i;
+        }
+
+        if (bb1.remaining() == 0)
+            return bb2.remaining() == 0 ? 0 : -1;
+
+        // bb1.remaining() > 0 && bb2.remaining() == 0
+        return 1;
+    }
+
+    public String getString(ByteBuffer bytes)
+    {
+        StringBuilder sb = new StringBuilder();
+        ByteBuffer bb = bytes.duplicate();
+        int i = 0;
+
+        while (bb.remaining() > 0)
+        {
+            if (bb.remaining() != bytes.remaining())
+                sb.append(":");
+
+            AbstractType comparator = getAndAppendNextComparator(i, bb, sb);
+            ByteBuffer value = getWithShortLength(bb);
+
+            sb.append(comparator.getString(value));
+
+            byte b = bb.get();
+            if (b != 0)
+            {
+                sb.append(":!");
+                break;
+            }
+            ++i;
+        }
+        return sb.toString();
+    }
+
+    /*
+     * FIXME: this would break if some of the component string representation
+     * contains ':'. None of our current comparator do so, so this is probably
+     * not an urgent matter, but this could break for custom comparator.
+     * (DynamicCompositeType would break on '@' too)
+     */
+    public ByteBuffer fromString(String source)
+    {
+        String[] parts = source.split(":");
+        List<ByteBuffer> components = new ArrayList<ByteBuffer>();
+        List<ParsedComparator> comparators = new ArrayList<ParsedComparator>();
+        int totalLength = 0, i = 0;
+        boolean lastByteIsOne = false;
+
+        for (String part : parts)
+        {
+            if (part.equals("!"))
+            {
+                lastByteIsOne = true;
+                break;
+            }
+
+            ParsedComparator p = parseNextComparator(i, part);
+            AbstractType type = p.getAbstractType();
+            part = p.getRemainingPart();
+
+            ByteBuffer component = type.fromString(part);
+            totalLength += p.getComparatorSerializedSize() + 2 + component.remaining() + 1;
+            components.add(component);
+            comparators.add(p);
+            ++i;
+        }
+
+        ByteBuffer bb = ByteBuffer.allocate(totalLength);
+        i = 0;
+        for (ByteBuffer component : components)
+        {
+            comparators.get(i).serializeComparator(bb);
+            putShortLength(bb, component.remaining());
+            bb.put(component); // it's ok to consume component as we won't use it anymore
+            bb.put((byte)0);
+            ++i;
+        }
+        if (lastByteIsOne)
+            bb.put(bb.limit() - 1, (byte)1);
+
+        bb.rewind();
+        return bb;
+    }
+
+    public void validate(ByteBuffer bytes) throws MarshalException
+    {
+        ByteBuffer bb = bytes.duplicate();
+
+        int i = 0;
+        while (bb.remaining() > 0)
+        {
+            AbstractType comparator = validateNextComparator(i, bb);
+
+            if (bb.remaining() < 2)
+                throw new MarshalException("Not enough bytes to read value size of component " + i);
+            int length = getShortLength(bb);
+
+            if (bb.remaining() < length)
+                throw new MarshalException("Not enough bytes to read value of component " + i);
+            ByteBuffer value = getBytes(bb, length);
+
+            comparator.validate(value);
+
+            if (bb.remaining() == 0)
+                throw new MarshalException("Not enough bytes to read the end-of-component byte of component" + i);
+            byte b = bb.get();
+            if (b != 0 && bb.remaining() != 0)
+                throw new MarshalException("Invalid bytes remaining after an end-of-component at component" + i);
+            ++i;
+        }
+    }
+
+    public ByteBuffer compose(ByteBuffer bytes)
+    {
+        return bytes;
+    }
+
+    public ByteBuffer decompose(ByteBuffer value)
+    {
+        return value;
+    }
+
+    public Class<ByteBuffer> getType()
+    {
+        return ByteBuffer.class;
+    }
+
+    public String toString(ByteBuffer value)
+    {
+        return getString(value);
+    }
+
+    /*
+     * JDBC metadata. For now we don't allow the use of compositeType with
+     * JDBC. We'll have to figure out what is the best solution here.
+     */
+    public boolean isSigned()
+    {
+        throw new UnsupportedOperationException("Not support for JDBC yet");
+    }
+
+    public boolean isCaseSensitive()
+    {
+        throw new UnsupportedOperationException("Not support for JDBC yet");
+    }
+
+    public boolean isCurrency()
+    {
+        throw new UnsupportedOperationException("Not support for JDBC yet");
+    }
+
+    public int getPrecision(ByteBuffer obj)
+    {
+        throw new UnsupportedOperationException("Not support for JDBC yet");
+    }
+
+    public int getScale(ByteBuffer obj)
+    {
+        throw new UnsupportedOperationException("Not support for JDBC yet");
+    }
+
+    public int getJdbcType()
+    {
+        throw new UnsupportedOperationException("Not support for JDBC yet");
+    }
+
+    public boolean needsQuotes()
+    {
+        throw new UnsupportedOperationException("Not support for JDBC yet");
+    }
+
+    abstract protected AbstractType getNextComparator(int i, ByteBuffer bb);
+    abstract protected AbstractType getNextComparator(int i, ByteBuffer bb1, ByteBuffer bb2);
+    abstract protected AbstractType getAndAppendNextComparator(int i, ByteBuffer bb, StringBuilder sb);
+    abstract protected ParsedComparator parseNextComparator(int i, String part);
+    abstract protected AbstractType validateNextComparator(int i, ByteBuffer bb) throws MarshalException;
+
+    protected static interface ParsedComparator
+    {
+        AbstractType getAbstractType();
+        String getRemainingPart();
+        int getComparatorSerializedSize();
+        void serializeComparator(ByteBuffer bb);
+    }
+}

Added: cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java?rev=1101120&view=auto
==============================================================================
--- cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java (added)
+++ cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/CompositeType.java Mon May  9 17:24:34 2011
@@ -0,0 +1,144 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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 org.apache.cassandra.db.marshal;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.utils.ByteBufferUtil;
+
+/*
+ * The encoding of a CompositeType column name should be:
+ *   <component><component><component> ...
+ * where <component> is:
+ *   <length of value><value><'end-of-component' byte>
+ * where <length of value> is a 2 bytes unsigned short the and the
+ * 'end-of-component' byte should always be 0 for actual column name.
+ * However, it can set to 1 for query bounds. This allows to query for the
+ * equivalent of 'give me the full super-column'. That is, if during a slice
+ * query uses:
+ *   start = <3><"foo".getBytes()><0>
+ *   end   = <3><"foo".getBytes()><1>
+ * then he will be sure to get *all* the columns whose first component is "foo".
+ * If for a component, the 'end-of-component' is != 0, there should not be any
+ * following component. The end-of-component can also be -1 to allow
+ * non-inclusive query. For instance:
+ *   start = <3><"foo".getBytes()><-1>
+ * allows to query everything that is greater than <3><"foo".getBytes()>, but
+ * not <3><"foo".getBytes()> itself.
+ */
+public class CompositeType extends AbstractCompositeType
+{
+    // package protected for unit tests sake
+    final List<AbstractType> types;
+
+    // interning instances
+    private static final Map<List<AbstractType>, CompositeType> instances = new HashMap<List<AbstractType>, CompositeType>();
+
+    public static CompositeType getInstance(TypeParser parser) throws ConfigurationException
+    {
+        return getInstance(parser.getTypeParameters());
+    }
+
+    public static synchronized CompositeType getInstance(List<AbstractType> types) throws ConfigurationException
+    {
+        if (types == null || types.isEmpty())
+            throw new ConfigurationException("Nonsensical empty parameter list for CompositeType");
+
+        CompositeType ct = instances.get(types);
+        if (ct == null)
+        {
+            ct = new CompositeType(types);
+            instances.put(types, ct);
+        }
+        return ct;
+    }
+
+    private CompositeType(List<AbstractType> types)
+    {
+        this.types = types;
+    }
+
+    protected AbstractType getNextComparator(int i, ByteBuffer bb)
+    {
+        return types.get(i);
+    }
+
+    protected AbstractType getNextComparator(int i, ByteBuffer bb1, ByteBuffer bb2)
+    {
+        return types.get(i);
+    }
+
+    protected AbstractType getAndAppendNextComparator(int i, ByteBuffer bb, StringBuilder sb)
+    {
+        return types.get(i);
+    }
+
+    protected ParsedComparator parseNextComparator(int i, String part)
+    {
+        return new StaticParsedComparator(types.get(i), part);
+    }
+
+    protected AbstractType validateNextComparator(int i, ByteBuffer bb) throws MarshalException
+    {
+        if (i >= types.size())
+            throw new MarshalException("Too many bytes for comparator");
+        return types.get(i);
+    }
+
+    private static class StaticParsedComparator implements ParsedComparator
+    {
+        final AbstractType type;
+        final String part;
+
+        StaticParsedComparator(AbstractType type, String part)
+        {
+            this.type = type;
+            this.part = part;
+        }
+
+        public AbstractType getAbstractType()
+        {
+            return type;
+        }
+
+        public String getRemainingPart()
+        {
+            return part;
+        }
+
+        public int getComparatorSerializedSize()
+        {
+            return 0;
+        }
+
+        public void serializeComparator(ByteBuffer bb) {}
+    }
+
+    @Override
+    public String toString()
+    {
+        return getClass().getName() + TypeParser.stringifyTypeParameters(types);
+    }
+}

Added: cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java?rev=1101120&view=auto
==============================================================================
--- cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java (added)
+++ cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/DynamicCompositeType.java Mon May  9 17:24:34 2011
@@ -0,0 +1,266 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF 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 org.apache.cassandra.db.marshal;
+
+import java.nio.charset.CharacterCodingException;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.utils.ByteBufferUtil;
+import org.apache.cassandra.utils.FBUtilities;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/*
+ * The encoding of a DynamicCompositeType column name should be:
+ *   <component><component><component> ...
+ * where <component> is:
+ *   <comparator part><value><'end-of-component' byte>
+ * where:
+ *   - <comparator part>: either the comparator full name, or a declared
+ *     aliases. This is at least 2 bytes (those 2 bytes are called header in
+ *     the following). If the first bit of the header is 1, then this
+ *     comparator part is an alias, otherwise it's a comparator full name:
+ *       - aliases: the actual alias is the 2nd byte of header taken as a
+ *         character. The whole <comparator part> is thus 2 byte long.
+ *       - comparator full name: the header is the length of the remaining
+ *         part. The remaining part is the UTF-8 encoded comparator class
+ *         name.
+ *   - <value>: the component value bytes preceded by 2 byte containing the
+ *     size of value (see CompositeType).
+ *   - 'end-of-component' byte is defined as in CompositeType
+ */
+public class DynamicCompositeType extends AbstractCompositeType
+{
+    private static final Logger logger = LoggerFactory.getLogger(DynamicCompositeType.class);
+
+    private final Map<Byte, AbstractType> aliases;
+
+    // interning instances
+    private static final Map<Map<Byte, AbstractType>, DynamicCompositeType> instances = new HashMap<Map<Byte, AbstractType>, DynamicCompositeType>();
+
+    public static synchronized DynamicCompositeType getInstance(TypeParser parser) throws ConfigurationException
+    {
+        return getInstance(parser.getAliasParameters());
+    }
+
+    public static synchronized DynamicCompositeType getInstance(Map<Byte, AbstractType> aliases)
+    {
+        DynamicCompositeType dct = instances.get(aliases);
+        if (dct == null)
+        {
+            dct = new DynamicCompositeType(aliases);
+            instances.put(aliases, dct);
+        }
+        return dct;
+    }
+
+    private DynamicCompositeType(Map<Byte, AbstractType> aliases)
+    {
+        this.aliases = aliases;
+    }
+
+    private AbstractType getComparator(ByteBuffer bb)
+    {
+        try
+        {
+            int header = getShortLength(bb);
+            if ((header & 0x8000) == 0)
+            {
+                String name = ByteBufferUtil.string(getBytes(bb, header));
+                return TypeParser.parse(name);
+            }
+            else
+            {
+                return aliases.get((byte)(header & 0xFF));
+            }
+        }
+        catch (CharacterCodingException e)
+        {
+            throw new RuntimeException(e);
+        }
+        catch (ConfigurationException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected AbstractType getNextComparator(int i, ByteBuffer bb)
+    {
+        return getComparator(bb);
+    }
+
+    protected AbstractType getNextComparator(int i, ByteBuffer bb1, ByteBuffer bb2)
+    {
+        AbstractType comp1 = getComparator(bb1);
+        AbstractType comp2 = getComparator(bb2);
+
+        // This rely on comparator always being singleton instances
+        if (comp1 != comp2)
+        {
+            logger.error("Mismatch between {} and {}", comp1, comp2);
+            throw new RuntimeException("Comparator mismatch while comparing two DynamicCompositeType colum name");
+        }
+        return comp1;
+    }
+
+    protected AbstractType getAndAppendNextComparator(int i, ByteBuffer bb, StringBuilder sb)
+    {
+        try
+        {
+            int header = getShortLength(bb);
+            if ((header & 0x8000) == 0)
+            {
+                String name = ByteBufferUtil.string(getBytes(bb, header));
+                sb.append(name).append("@");
+                return TypeParser.parse(name);
+            }
+            else
+            {
+                sb.append((char)(header & 0xFF)).append("@");
+                return aliases.get((byte)(header & 0xFF));
+            }
+        }
+        catch (CharacterCodingException e)
+        {
+            throw new RuntimeException(e);
+        }
+        catch (ConfigurationException e)
+        {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected ParsedComparator parseNextComparator(int i, String part)
+    {
+        return new DynamicParsedComparator(part);
+    }
+
+    protected AbstractType validateNextComparator(int i, ByteBuffer bb) throws MarshalException
+    {
+        AbstractType comparator = null;
+        if (bb.remaining() < 2)
+            throw new MarshalException("Not enough bytes to header of the comparator part of component " + i);
+        int header = getShortLength(bb);
+        if ((header & 0x8000) == 0)
+        {
+            if (bb.remaining() < header)
+                throw new MarshalException("Not enough bytes to read comparator name of component " + i);
+
+            ByteBuffer value = getBytes(bb, header);
+            try
+            {
+                comparator = TypeParser.parse(ByteBufferUtil.string(value));
+            }
+            catch (Exception e)
+            {
+                // we'll deal with this below since comparator == null
+            }
+        }
+        else
+        {
+            comparator = aliases.get((byte)(header & 0xFF));
+        }
+
+        if (comparator == null)
+            throw new MarshalException("Cannot find comparator for component " + i);
+        else
+            return comparator;
+    }
+
+    private class DynamicParsedComparator implements ParsedComparator
+    {
+        final AbstractType type;
+        final boolean isAlias;
+        final String comparatorName;
+        final String remainingPart;
+
+        DynamicParsedComparator(String part)
+        {
+            String[] splits = part.split("@");
+            if (splits.length != 2)
+                throw new IllegalArgumentException("Invalid component representation: " + part);
+
+            comparatorName = splits[0];
+            remainingPart = splits[1];
+
+            try
+            {
+                AbstractType t = null;
+                if (comparatorName.length() == 1)
+                {
+                    // try for an alias
+                    // Note: the char to byte cast is theorically bogus for unicode character. I take full
+                    // responsibility if someone get hit by this (without making it on purpose)
+                    t = aliases.get((byte)comparatorName.charAt(0));
+                }
+                isAlias = t != null;
+                if (!isAlias)
+                {
+                    t = TypeParser.parse(comparatorName);
+                }
+                type = t;
+            }
+            catch (ConfigurationException e)
+            {
+                throw new IllegalArgumentException(e);
+            }
+        }
+
+        public AbstractType getAbstractType()
+        {
+            return type;
+        }
+
+        public String getRemainingPart()
+        {
+            return remainingPart;
+        }
+
+        public int getComparatorSerializedSize()
+        {
+            return isAlias ? 2 : 2 + ByteBufferUtil.bytes(comparatorName).remaining();
+        }
+
+        public void serializeComparator(ByteBuffer bb)
+        {
+            int header = 0;
+            if (isAlias)
+                header = 0x8000 | ((byte)comparatorName.charAt(0));
+            else
+                header = comparatorName.length();
+            putShortLength(bb, header);
+
+            if (!isAlias)
+                bb.put(ByteBufferUtil.bytes(comparatorName));
+        }
+    }
+
+    @Override
+    public String toString()
+    {
+        return getClass().getName() + TypeParser.stringifyAliasesParameters(aliases);
+    }
+}

Modified: cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java?rev=1101120&r1=1101119&r2=1101120&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java (original)
+++ cassandra/branches/cassandra-0.8.1/src/java/org/apache/cassandra/db/marshal/TypeParser.java Mon May  9 17:24:34 2011
@@ -27,6 +27,8 @@ import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.lang.StringUtils;
+
 import org.apache.cassandra.config.ConfigurationException;
 import org.apache.cassandra.utils.FBUtilities;
 
@@ -414,17 +416,7 @@ public class TypeParser
     public static String stringifyTypeParameters(List<AbstractType> types)
     {
         StringBuilder sb = new StringBuilder();
-        sb.append('(');
-        Iterator<AbstractType> iter = types.iterator();
-        if (iter.hasNext())
-        {
-            sb.append(iter.next());
-        }
-        while (iter.hasNext())
-        {
-            sb.append(',').append(iter.next());
-        }
-        sb.append(')');
+        sb.append('(').append(StringUtils.join(types, ",")).append(')');
         return sb.toString();
     }
 }

Modified: cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java?rev=1101120&r1=1101119&r2=1101120&view=diff
==============================================================================
--- cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java (original)
+++ cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/SchemaLoader.java Mon May  9 17:24:34 2011
@@ -54,7 +54,7 @@ public class SchemaLoader
         }
     }
 
-    public static Collection<KSMetaData> schemaDefinition()
+    public static Collection<KSMetaData> schemaDefinition() throws ConfigurationException
     {
         List<KSMetaData> schema = new ArrayList<KSMetaData>();
 
@@ -78,6 +78,12 @@ public class SchemaLoader
         ColumnFamilyType st = ColumnFamilyType.Standard;
         ColumnFamilyType su = ColumnFamilyType.Super;
         AbstractType bytes = BytesType.instance;
+
+        AbstractType composite = CompositeType.getInstance(Arrays.asList(new AbstractType[]{BytesType.instance, TimeUUIDType.instance, IntegerType.instance}));
+        Map<Byte, AbstractType> aliases = new HashMap<Byte, AbstractType>();
+        aliases.put((byte)'b', BytesType.instance);
+        aliases.put((byte)'t', TimeUUIDType.instance);
+        AbstractType dynamicComposite = DynamicCompositeType.getInstance(aliases);
       
         // these column definitions will will be applied to the jdbc utf and integer column familes respectively.
         Map<ByteBuffer, ColumnDefinition> integerColumn = new HashMap<ByteBuffer, ColumnDefinition>();
@@ -134,7 +140,17 @@ public class SchemaLoader
                                   jdbcCFMD(ks1, "JdbcUtf8", UTF8Type.instance).columnMetadata(utf8Column),
                                   jdbcCFMD(ks1, "JdbcLong", LongType.instance),
                                   jdbcCFMD(ks1, "JdbcBytes", bytes),
-                                  jdbcCFMD(ks1, "JdbcAscii", AsciiType.instance)));
+                                  jdbcCFMD(ks1, "JdbcAscii", AsciiType.instance),
+                                  new CFMetaData(ks1,
+                                                 "StandardComposite",
+                                                 st,
+                                                 composite,
+                                                 null),
+                                  new CFMetaData(ks1,
+                                                 "StandardDynamicComposite",
+                                                 st,
+                                                 dynamicComposite,
+                                                 null)));
 
         // Keyspace 2
         schema.add(new KSMetaData(ks2,

Added: cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java?rev=1101120&view=auto
==============================================================================
--- cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java (added)
+++ cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/CompositeTypeTest.java Mon May  9 17:24:34 2011
@@ -0,0 +1,268 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF 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 org.apache.cassandra.db.marshal;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.UUID;
+
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+import org.apache.cassandra.CleanupHelper;
+import org.apache.cassandra.Util;
+import org.apache.cassandra.config.ConfigurationException;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.columniterator.IdentityQueryFilter;
+import org.apache.cassandra.db.filter.QueryFilter;
+import org.apache.cassandra.db.filter.QueryPath;
+import org.apache.cassandra.utils.*;
+
+public class CompositeTypeTest extends CleanupHelper
+{
+    private static final String cfName = "StandardComposite";
+    private static final CompositeType comparator;
+    static
+    {
+        List<AbstractType> subComparators = new ArrayList<AbstractType>();
+        subComparators.add(BytesType.instance);
+        subComparators.add(TimeUUIDType.instance);
+        subComparators.add(IntegerType.instance);
+        try
+        {
+            comparator = CompositeType.getInstance(subComparators);
+        }
+        catch (ConfigurationException e)
+        {
+            throw new RuntimeException(e);
+        }
+
+    }
+
+    private static final int UUID_COUNT = 3;
+    private static final UUID[] uuids = new UUID[UUID_COUNT];
+    static
+    {
+        for (int i = 0; i < UUID_COUNT; ++i)
+            uuids[i] = UUIDGen.makeType1UUIDFromHost(FBUtilities.getLocalAddress());
+    }
+
+    @Test
+    public void testEndOfComponent()
+    {
+        ByteBuffer[] cnames = {
+            createCompositeKey("test1", uuids[0], -1, false),
+            createCompositeKey("test1", uuids[1], 24, false),
+            createCompositeKey("test1", uuids[1], 42, false),
+            createCompositeKey("test1", uuids[1], 83, false),
+            createCompositeKey("test1", uuids[2], -1, false),
+            createCompositeKey("test1", uuids[2], 42, false),
+        };
+
+        ByteBuffer start = createCompositeKey("test1", uuids[1], -1, false);
+        ByteBuffer stop = createCompositeKey("test1", uuids[1], -1, true);
+
+        for (int i = 0; i < 1; ++i)
+        {
+            assert comparator.compare(start, cnames[i]) > 0;
+            assert comparator.compare(stop, cnames[i]) > 0;
+        }
+        for (int i = 1; i < 4; ++i)
+        {
+            assert comparator.compare(start, cnames[i]) < 0;
+            assert comparator.compare(stop, cnames[i]) > 0;
+        }
+        for (int i = 4; i < cnames.length; ++i)
+        {
+            assert comparator.compare(start, cnames[i]) < 0;
+            assert comparator.compare(stop, cnames[i]) < 0;
+        }
+    }
+
+    @Test
+    public void testGetString()
+    {
+        String test1Hex = ByteBufferUtil.bytesToHex(ByteBufferUtil.bytes("test1"));
+        ByteBuffer key = createCompositeKey("test1", uuids[1], 42, false);
+        assert comparator.getString(key).equals(test1Hex + ":" + uuids[1] + ":42");
+
+        key = createCompositeKey("test1", uuids[1], -1, true);
+        assert comparator.getString(key).equals(test1Hex + ":" + uuids[1] + ":!");
+    }
+
+    @Test
+    public void testFromString()
+    {
+        String test1Hex = ByteBufferUtil.bytesToHex(ByteBufferUtil.bytes("test1"));
+        ByteBuffer key = createCompositeKey("test1", uuids[1], 42, false);
+        assert key.equals(comparator.fromString(test1Hex + ":" + uuids[1] + ":42"));
+
+        key = createCompositeKey("test1", uuids[1], -1, true);
+        assert key.equals(comparator.fromString(test1Hex + ":" + uuids[1] + ":!"));
+    }
+
+    @Test
+    public void testValidate()
+    {
+        ByteBuffer key = createCompositeKey("test1", uuids[1], 42, false);
+        comparator.validate(key);
+
+        key = createCompositeKey("test1", null, -1, false);
+        comparator.validate(key);
+
+        key = createCompositeKey("test1", uuids[2], -1, true);
+        comparator.validate(key);
+
+        key.get(); // make sure we're not aligned anymore
+        try
+        {
+            comparator.validate(key);
+            fail("Should not validate");
+        }
+        catch (MarshalException e) {}
+
+        key = ByteBuffer.allocate(3 + "test1".length() + 3 + 14);
+        key.putShort((short) "test1".length());
+        key.put(ByteBufferUtil.bytes("test1"));
+        key.put((byte) 0);
+        key.putShort((short) 14);
+        key.rewind();
+        try
+        {
+            comparator.validate(key);
+            fail("Should not validate");
+        }
+        catch (MarshalException e)
+        {
+            assert e.toString().contains("TimeUUID should be 16 or 0 bytes");
+        }
+
+        key = createCompositeKey("test1", UUID.randomUUID(), 42, false);
+        try
+        {
+            comparator.validate(key);
+            fail("Should not validate");
+        }
+        catch (MarshalException e)
+        {
+            assert e.toString().contains("Invalid version for TimeUUID type");
+        }
+    }
+
+    @Test
+    public void testFullRound() throws Exception
+    {
+        Table table = Table.open("Keyspace1");
+        ColumnFamilyStore cfs = table.getColumnFamilyStore(cfName);
+
+        ByteBuffer cname1 = createCompositeKey("test1", null, -1, false);
+        ByteBuffer cname2 = createCompositeKey("test1", uuids[0], 24, false);
+        ByteBuffer cname3 = createCompositeKey("test1", uuids[0], 42, false);
+        ByteBuffer cname4 = createCompositeKey("test2", uuids[0], -1, false);
+        ByteBuffer cname5 = createCompositeKey("test2", uuids[1], 42, false);
+
+        ByteBuffer key = ByteBufferUtil.bytes("k");
+        RowMutation rm = new RowMutation("Keyspace1", key);
+        addColumn(rm, cname5);
+        addColumn(rm, cname1);
+        addColumn(rm, cname4);
+        addColumn(rm, cname2);
+        addColumn(rm, cname3);
+        rm.apply();
+
+        ColumnFamily cf = cfs.getColumnFamily(QueryFilter.getIdentityFilter(Util.dk("k"), new QueryPath(cfName, null, null)));
+
+        Iterator<IColumn> iter = cf.getSortedColumns().iterator();
+
+        assert iter.next().name().equals(cname1);
+        assert iter.next().name().equals(cname2);
+        assert iter.next().name().equals(cname3);
+        assert iter.next().name().equals(cname4);
+        assert iter.next().name().equals(cname5);
+    }
+
+    @Test
+    public void testEmptyParametersNotallowed()
+    {
+        try
+        {
+            TypeParser.parse("CompositeType");
+            fail("Shouldn't work");
+        }
+        catch (ConfigurationException e) {}
+
+        try
+        {
+            TypeParser.parse("CompositeType()");
+            fail("Shouldn't work");
+        }
+        catch (ConfigurationException e) {}
+    }
+
+
+    private void addColumn(RowMutation rm, ByteBuffer cname)
+    {
+        rm.add(new QueryPath(cfName, null , cname), ByteBufferUtil.EMPTY_BYTE_BUFFER, 0);
+    }
+
+    private ByteBuffer createCompositeKey(String s, UUID uuid, int i, boolean lastIsOne)
+    {
+        ByteBuffer bytes = ByteBufferUtil.bytes(s);
+        int totalSize = 0;
+        if (s != null)
+        {
+            totalSize += 2 + bytes.remaining() + 1;
+            if (uuid != null)
+            {
+                totalSize += 2 + 16 + 1;
+                if (i != -1)
+                {
+                    totalSize += 2 + 1 + 1;
+                }
+            }
+        }
+
+        ByteBuffer bb = ByteBuffer.allocate(totalSize);
+
+        if (s != null)
+        {
+            bb.putShort((short) bytes.remaining());
+            bb.put(bytes);
+            bb.put(uuid == null && lastIsOne ? (byte)1 : (byte)0);
+            if (uuid != null)
+            {
+                bb.putShort((short) 16);
+                bb.put(UUIDGen.decompose(uuid));
+                bb.put(i == -1 && lastIsOne ? (byte)1 : (byte)0);
+                if (i != -1)
+                {
+                    // We are putting a byte only because our test use ints that fit in a byte *and* IntegerType.fromString() will
+                    // return something compatible (i.e, putting a full int here would break 'fromStringTest')
+                    bb.putShort((short) 1);
+                    bb.put((byte)i);
+                    bb.put(lastIsOne ? (byte)1 : (byte)0);
+                }
+            }
+        }
+        bb.rewind();
+        return bb;
+    }
+}

Added: cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java
URL: http://svn.apache.org/viewvc/cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java?rev=1101120&view=auto
==============================================================================
--- cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java (added)
+++ cassandra/branches/cassandra-0.8.1/test/unit/org/apache/cassandra/db/marshal/DynamicCompositeTypeTest.java Mon May  9 17:24:34 2011
@@ -0,0 +1,246 @@
+/*
+* Licensed to the Apache Software Foundation (ASF) under one
+* or more contributor license agreements.  See the NOTICE file
+* distributed with this work for additional information
+* regarding copyright ownership.  The ASF 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 org.apache.cassandra.db.marshal;
+
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.UUID;
+
+import org.junit.Test;
+import static org.junit.Assert.fail;
+
+import org.apache.cassandra.CleanupHelper;
+import org.apache.cassandra.Util;
+import org.apache.cassandra.db.*;
+import org.apache.cassandra.db.columniterator.IdentityQueryFilter;
+import org.apache.cassandra.db.filter.QueryFilter;
+import org.apache.cassandra.db.filter.QueryPath;
+import org.apache.cassandra.utils.*;
+
+public class DynamicCompositeTypeTest extends CleanupHelper
+{
+    private static final String cfName = "StandardDynamicComposite";
+
+    private static final DynamicCompositeType comparator;
+    static
+    {
+        Map<Byte, AbstractType> aliases = new HashMap<Byte, AbstractType>();
+        aliases.put((byte)'b', BytesType.instance);
+        aliases.put((byte)'t', TimeUUIDType.instance);
+        comparator = DynamicCompositeType.getInstance(aliases);
+    }
+
+    private static final int UUID_COUNT = 3;
+    private static final UUID[] uuids = new UUID[UUID_COUNT];
+    static
+    {
+        for (int i = 0; i < UUID_COUNT; ++i)
+            uuids[i] = UUIDGen.makeType1UUIDFromHost(FBUtilities.getLocalAddress());
+    }
+
+    @Test
+    public void testEndOfComponent()
+    {
+        ByteBuffer[] cnames = {
+            createDynamicCompositeKey("test1", uuids[0], -1, false),
+            createDynamicCompositeKey("test1", uuids[1], 24, false),
+            createDynamicCompositeKey("test1", uuids[1], 42, false),
+            createDynamicCompositeKey("test1", uuids[1], 83, false),
+            createDynamicCompositeKey("test1", uuids[2], -1, false),
+            createDynamicCompositeKey("test1", uuids[2], 42, false),
+        };
+
+        ByteBuffer start = createDynamicCompositeKey("test1", uuids[1], -1, false);
+        ByteBuffer stop = createDynamicCompositeKey("test1", uuids[1], -1, true);
+
+        for (int i = 0; i < 1; ++i)
+        {
+            assert comparator.compare(start, cnames[i]) > 0;
+            assert comparator.compare(stop, cnames[i]) > 0;
+        }
+        for (int i = 1; i < 4; ++i)
+        {
+            assert comparator.compare(start, cnames[i]) < 0;
+            assert comparator.compare(stop, cnames[i]) > 0;
+        }
+        for (int i = 4; i < cnames.length; ++i)
+        {
+            assert comparator.compare(start, cnames[i]) < 0;
+            assert comparator.compare(stop, cnames[i]) < 0;
+        }
+    }
+
+    @Test
+    public void testGetString()
+    {
+        String test1Hex = ByteBufferUtil.bytesToHex(ByteBufferUtil.bytes("test1"));
+        ByteBuffer key = createDynamicCompositeKey("test1", uuids[1], 42, false);
+        assert comparator.getString(key).equals("b@" + test1Hex + ":t@" + uuids[1] + ":IntegerType@42");
+
+        key = createDynamicCompositeKey("test1", uuids[1], -1, true);
+        assert comparator.getString(key).equals("b@" + test1Hex + ":t@" + uuids[1] + ":!");
+    }
+
+    @Test
+    public void testFromString()
+    {
+        String test1Hex = ByteBufferUtil.bytesToHex(ByteBufferUtil.bytes("test1"));
+        ByteBuffer key = createDynamicCompositeKey("test1", uuids[1], 42, false);
+        assert key.equals(comparator.fromString("b@" + test1Hex + ":t@" + uuids[1] + ":IntegerType@42"));
+
+        key = createDynamicCompositeKey("test1", uuids[1], -1, true);
+        assert key.equals(comparator.fromString("b@" + test1Hex + ":t@" + uuids[1] + ":!"));
+    }
+
+    @Test
+    public void testValidate()
+    {
+        ByteBuffer key = createDynamicCompositeKey("test1", uuids[1], 42, false);
+        comparator.validate(key);
+
+        key = createDynamicCompositeKey("test1", null, -1, false);
+        comparator.validate(key);
+
+        key = createDynamicCompositeKey("test1", uuids[2], -1, true);
+        comparator.validate(key);
+
+        key.get(); // make sure we're not aligned anymore
+        try
+        {
+            comparator.validate(key);
+            fail("Should not validate");
+        }
+        catch (MarshalException e) {}
+
+        key = ByteBuffer.allocate(5 + "test1".length() + 5 + 14);
+        key.putShort((short) (0x8000 | 'b'));
+        key.putShort((short) "test1".length());
+        key.put(ByteBufferUtil.bytes("test1"));
+        key.put((byte) 0);
+        key.putShort((short) (0x8000 | 't'));
+        key.putShort((short) 14);
+        key.rewind();
+        try
+        {
+            comparator.validate(key);
+            fail("Should not validate");
+        }
+        catch (MarshalException e)
+        {
+            assert e.toString().contains("TimeUUID should be 16 or 0 bytes");
+        }
+
+        key = createDynamicCompositeKey("test1", UUID.randomUUID(), 42, false);
+        try
+        {
+            comparator.validate(key);
+            fail("Should not validate");
+        }
+        catch (MarshalException e)
+        {
+            assert e.toString().contains("Invalid version for TimeUUID type");
+        }
+    }
+
+    @Test
+    public void testFullRound() throws Exception
+    {
+        Table table = Table.open("Keyspace1");
+        ColumnFamilyStore cfs = table.getColumnFamilyStore(cfName);
+
+        ByteBuffer cname1 = createDynamicCompositeKey("test1", null, -1, false);
+        ByteBuffer cname2 = createDynamicCompositeKey("test1", uuids[0], 24, false);
+        ByteBuffer cname3 = createDynamicCompositeKey("test1", uuids[0], 42, false);
+        ByteBuffer cname4 = createDynamicCompositeKey("test2", uuids[0], -1, false);
+        ByteBuffer cname5 = createDynamicCompositeKey("test2", uuids[1], 42, false);
+
+        ByteBuffer key = ByteBufferUtil.bytes("k");
+        RowMutation rm = new RowMutation("Keyspace1", key);
+        addColumn(rm, cname5);
+        addColumn(rm, cname1);
+        addColumn(rm, cname4);
+        addColumn(rm, cname2);
+        addColumn(rm, cname3);
+        rm.apply();
+
+        ColumnFamily cf = cfs.getColumnFamily(QueryFilter.getIdentityFilter(Util.dk("k"), new QueryPath(cfName, null, null)));
+
+        Iterator<IColumn> iter = cf.getSortedColumns().iterator();
+
+        assert iter.next().name().equals(cname1);
+        assert iter.next().name().equals(cname2);
+        assert iter.next().name().equals(cname3);
+        assert iter.next().name().equals(cname4);
+        assert iter.next().name().equals(cname5);
+    }
+
+    private void addColumn(RowMutation rm, ByteBuffer cname)
+    {
+        rm.add(new QueryPath(cfName, null , cname), ByteBufferUtil.EMPTY_BYTE_BUFFER, 0);
+    }
+
+    private ByteBuffer createDynamicCompositeKey(String s, UUID uuid, int i, boolean lastIsOne)
+    {
+        ByteBuffer bytes = ByteBufferUtil.bytes(s);
+        int totalSize = 0;
+        if (s != null)
+        {
+            totalSize += 2 + 2 + bytes.remaining() + 1;
+            if (uuid != null)
+            {
+                totalSize += 2 + 2 + 16 + 1;
+                if (i != -1)
+                {
+                    totalSize += 2 + "IntegerType".length() + 2 + 1 + 1;
+                }
+            }
+        }
+
+        ByteBuffer bb = ByteBuffer.allocate(totalSize);
+
+        if (s != null)
+        {
+            bb.putShort((short)(0x8000 | 'b'));
+            bb.putShort((short) bytes.remaining());
+            bb.put(bytes);
+            bb.put(uuid == null && lastIsOne ? (byte)1 : (byte)0);
+            if (uuid != null)
+            {
+                bb.putShort((short)(0x8000 | 't'));
+                bb.putShort((short) 16);
+                bb.put(UUIDGen.decompose(uuid));
+                bb.put(i == -1 && lastIsOne ? (byte)1 : (byte)0);
+                if (i != -1)
+                {
+                    bb.putShort((short) "IntegerType".length());
+                    bb.put(ByteBufferUtil.bytes("IntegerType"));
+                    // We are putting a byte only because our test use ints that fit in a byte *and* IntegerType.fromString() will
+                    // return something compatible (i.e, putting a full int here would break 'fromStringTest')
+                    bb.putShort((short) 1);
+                    bb.put((byte)i);
+                    bb.put(lastIsOne ? (byte)1 : (byte)0);
+                }
+            }
+        }
+        bb.rewind();
+        return bb;
+    }
+}



Mime
View raw message