| /* |
| * 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.composites; |
| |
| import java.io.DataInput; |
| import java.io.IOException; |
| import java.nio.ByteBuffer; |
| import java.util.Comparator; |
| |
| import org.apache.cassandra.db.Cell; |
| import org.apache.cassandra.db.DeletionInfo; |
| import org.apache.cassandra.db.NativeCell; |
| import org.apache.cassandra.db.RangeTombstone; |
| import org.apache.cassandra.db.TypeSizes; |
| import org.apache.cassandra.db.filter.ColumnSlice; |
| import org.apache.cassandra.db.filter.SliceQueryFilter; |
| import org.apache.cassandra.db.marshal.AbstractCompositeType; |
| import org.apache.cassandra.db.marshal.AbstractType; |
| import org.apache.cassandra.io.IVersionedSerializer; |
| import org.apache.cassandra.io.util.DataOutputPlus; |
| import org.apache.cassandra.utils.ByteBufferUtil; |
| |
| import static org.apache.cassandra.io.sstable.IndexHelper.IndexInfo; |
| |
| public abstract class AbstractCType implements CType |
| { |
| static final Comparator<Cell> rightNativeCell = new Comparator<Cell>() |
| { |
| public int compare(Cell o1, Cell o2) |
| { |
| return -((NativeCell) o2).compareTo(o1.name()); |
| } |
| }; |
| |
| static final Comparator<Cell> neitherNativeCell = new Comparator<Cell>() |
| { |
| public int compare(Cell o1, Cell o2) |
| { |
| return compareUnsigned(o1.name(), o2.name()); |
| } |
| }; |
| |
| // only one or the other of these will ever be used |
| static final Comparator<Object> asymmetricRightNativeCell = new Comparator<Object>() |
| { |
| public int compare(Object o1, Object o2) |
| { |
| return -((NativeCell) o2).compareTo((Composite) o1); |
| } |
| }; |
| |
| static final Comparator<Object> asymmetricNeitherNativeCell = new Comparator<Object>() |
| { |
| public int compare(Object o1, Object o2) |
| { |
| return compareUnsigned((Composite) o1, ((Cell) o2).name()); |
| } |
| }; |
| |
| private final Comparator<Composite> reverseComparator; |
| private final Comparator<IndexInfo> indexComparator; |
| private final Comparator<IndexInfo> indexReverseComparator; |
| |
| private final Serializer serializer; |
| |
| private final IVersionedSerializer<ColumnSlice> sliceSerializer; |
| private final IVersionedSerializer<SliceQueryFilter> sliceQueryFilterSerializer; |
| private final DeletionInfo.Serializer deletionInfoSerializer; |
| private final RangeTombstone.Serializer rangeTombstoneSerializer; |
| |
| protected final boolean isByteOrderComparable; |
| |
| protected AbstractCType(boolean isByteOrderComparable) |
| { |
| reverseComparator = new Comparator<Composite>() |
| { |
| public int compare(Composite c1, Composite c2) |
| { |
| return AbstractCType.this.compare(c2, c1); |
| } |
| }; |
| indexComparator = new Comparator<IndexInfo>() |
| { |
| public int compare(IndexInfo o1, IndexInfo o2) |
| { |
| return AbstractCType.this.compare(o1.lastName, o2.lastName); |
| } |
| }; |
| indexReverseComparator = new Comparator<IndexInfo>() |
| { |
| public int compare(IndexInfo o1, IndexInfo o2) |
| { |
| return AbstractCType.this.compare(o1.firstName, o2.firstName); |
| } |
| }; |
| |
| serializer = new Serializer(this); |
| |
| sliceSerializer = new ColumnSlice.Serializer(this); |
| sliceQueryFilterSerializer = new SliceQueryFilter.Serializer(this); |
| deletionInfoSerializer = new DeletionInfo.Serializer(this); |
| rangeTombstoneSerializer = new RangeTombstone.Serializer(this); |
| this.isByteOrderComparable = isByteOrderComparable; |
| } |
| |
| protected static boolean isByteOrderComparable(Iterable<AbstractType<?>> types) |
| { |
| boolean isByteOrderComparable = true; |
| for (AbstractType<?> type : types) |
| isByteOrderComparable &= type.isByteOrderComparable(); |
| return isByteOrderComparable; |
| } |
| |
| static int compareUnsigned(Composite c1, Composite c2) |
| { |
| if (c1.isStatic() != c2.isStatic()) |
| { |
| // Static sorts before non-static no matter what, except for empty which |
| // always sort first |
| if (c1.isEmpty()) |
| return c2.isEmpty() ? 0 : -1; |
| if (c2.isEmpty()) |
| return 1; |
| return c1.isStatic() ? -1 : 1; |
| } |
| |
| int s1 = c1.size(); |
| int s2 = c2.size(); |
| int minSize = Math.min(s1, s2); |
| |
| for (int i = 0; i < minSize; i++) |
| { |
| int cmp = ByteBufferUtil.compareUnsigned(c1.get(i), c2.get(i)); |
| if (cmp != 0) |
| return cmp; |
| } |
| |
| if (s1 == s2) |
| return c1.eoc().compareTo(c2.eoc()); |
| return s1 < s2 ? c1.eoc().prefixComparisonResult : -c2.eoc().prefixComparisonResult; |
| } |
| |
| public int compare(Composite c1, Composite c2) |
| { |
| if (c1.isStatic() != c2.isStatic()) |
| { |
| // Static sorts before non-static no matter what, except for empty which |
| // always sort first |
| if (c1.isEmpty()) |
| return c2.isEmpty() ? 0 : -1; |
| if (c2.isEmpty()) |
| return 1; |
| return c1.isStatic() ? -1 : 1; |
| } |
| |
| int s1 = c1.size(); |
| int s2 = c2.size(); |
| int minSize = Math.min(s1, s2); |
| |
| for (int i = 0; i < minSize; i++) |
| { |
| int cmp = isByteOrderComparable |
| ? ByteBufferUtil.compareUnsigned(c1.get(i), c2.get(i)) |
| : subtype(i).compare(c1.get(i), c2.get(i)); |
| if (cmp != 0) |
| return cmp; |
| } |
| |
| if (s1 == s2) |
| return c1.eoc().compareTo(c2.eoc()); |
| return s1 < s2 ? c1.eoc().prefixComparisonResult : -c2.eoc().prefixComparisonResult; |
| } |
| |
| protected Comparator<Cell> getByteOrderColumnComparator(boolean isRightNative) |
| { |
| if (isRightNative) |
| return rightNativeCell; |
| return neitherNativeCell; |
| } |
| |
| protected Comparator<Object> getByteOrderAsymmetricColumnComparator(boolean isRightNative) |
| { |
| if (isRightNative) |
| return asymmetricRightNativeCell; |
| return asymmetricNeitherNativeCell; |
| } |
| |
| public void validate(Composite name) |
| { |
| ByteBuffer previous = null; |
| for (int i = 0; i < name.size(); i++) |
| { |
| AbstractType<?> comparator = subtype(i); |
| ByteBuffer value = name.get(i); |
| comparator.validateCollectionMember(value, previous); |
| previous = value; |
| } |
| } |
| |
| public boolean isCompatibleWith(CType previous) |
| { |
| if (this == previous) |
| return true; |
| |
| // Extending with new components is fine, shrinking is not |
| if (size() < previous.size()) |
| return false; |
| |
| for (int i = 0; i < previous.size(); i++) |
| { |
| AbstractType<?> tprev = previous.subtype(i); |
| AbstractType<?> tnew = subtype(i); |
| if (!tnew.isCompatibleWith(tprev)) |
| return false; |
| } |
| return true; |
| } |
| |
| public String getString(Composite c) |
| { |
| StringBuilder sb = new StringBuilder(); |
| for (int i = 0; i < c.size(); i++) |
| { |
| if (i > 0) |
| sb.append(":"); |
| sb.append(AbstractCompositeType.escape(subtype(i).getString(c.get(i)))); |
| } |
| switch (c.eoc()) |
| { |
| case START: |
| sb.append(":_"); |
| break; |
| case END: |
| sb.append(":!"); |
| break; |
| } |
| return sb.toString(); |
| } |
| |
| public Composite make(Object... components) |
| { |
| if (components.length > size()) |
| throw new IllegalArgumentException("Too many components, max is " + size()); |
| |
| CBuilder builder = builder(); |
| for (int i = 0; i < components.length; i++) |
| { |
| Object obj = components[i]; |
| if (obj instanceof ByteBuffer) |
| builder.add((ByteBuffer)obj); |
| else |
| builder.add(obj); |
| } |
| return builder.build(); |
| } |
| |
| public CType.Serializer serializer() |
| { |
| return serializer; |
| } |
| |
| public Comparator<Composite> reverseComparator() |
| { |
| return reverseComparator; |
| } |
| |
| public Comparator<IndexInfo> indexComparator() |
| { |
| return indexComparator; |
| } |
| |
| public Comparator<IndexInfo> indexReverseComparator() |
| { |
| return indexReverseComparator; |
| } |
| |
| public IVersionedSerializer<ColumnSlice> sliceSerializer() |
| { |
| return sliceSerializer; |
| } |
| |
| public IVersionedSerializer<SliceQueryFilter> sliceQueryFilterSerializer() |
| { |
| return sliceQueryFilterSerializer; |
| } |
| |
| public DeletionInfo.Serializer deletionInfoSerializer() |
| { |
| return deletionInfoSerializer; |
| } |
| |
| public RangeTombstone.Serializer rangeTombstoneSerializer() |
| { |
| return rangeTombstoneSerializer; |
| } |
| |
| @Override |
| public boolean equals(Object o) |
| { |
| if (this == o) |
| return true; |
| |
| if (o == null) |
| return false; |
| |
| if (!getClass().equals(o.getClass())) |
| return false; |
| |
| CType c = (CType)o; |
| if (size() != c.size()) |
| return false; |
| |
| for (int i = 0; i < size(); i++) |
| { |
| if (!subtype(i).equals(c.subtype(i))) |
| return false; |
| } |
| return true; |
| } |
| |
| @Override |
| public int hashCode() |
| { |
| int h = 31; |
| for (int i = 0; i < size(); i++) |
| h += subtype(i).hashCode(); |
| return h + getClass().hashCode(); |
| } |
| |
| @Override |
| public String toString() |
| { |
| return asAbstractType().toString(); |
| } |
| |
| protected static ByteBuffer sliceBytes(ByteBuffer bb, int offs, int length) |
| { |
| ByteBuffer copy = bb.duplicate(); |
| copy.position(offs); |
| copy.limit(offs + length); |
| return copy; |
| } |
| |
| protected static void checkRemaining(ByteBuffer bb, int offs, int length) |
| { |
| if (offs + length > bb.limit()) |
| throw new IllegalArgumentException(String.format("Not enough bytes. Offset: %d. Length: %d. Buffer size: %d", |
| offs, length, bb.limit())); |
| } |
| |
| private static class Serializer implements CType.Serializer |
| { |
| private final CType type; |
| |
| public Serializer(CType type) |
| { |
| this.type = type; |
| } |
| |
| public void serialize(Composite c, DataOutputPlus out) throws IOException |
| { |
| ByteBufferUtil.writeWithShortLength(c.toByteBuffer(), out); |
| } |
| |
| public Composite deserialize(DataInput in) throws IOException |
| { |
| return type.fromByteBuffer(ByteBufferUtil.readWithShortLength(in)); |
| } |
| |
| public long serializedSize(Composite c, TypeSizes type) |
| { |
| return type.sizeofWithShortLength(c.toByteBuffer()); |
| } |
| |
| public void skip(DataInput in) throws IOException |
| { |
| ByteBufferUtil.skipShortLength(in); |
| } |
| } |
| } |