/*
* 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.usergrid.mq.cassandra;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.uuid.UUIDComparator;
import me.prettyprint.hector.api.beans.DynamicComposite;
import me.prettyprint.hector.api.mutation.Mutator;
import static java.nio.ByteBuffer.wrap;
import static org.apache.usergrid.utils.JsonUtils.toJsonNode;
import static org.apache.usergrid.utils.UUIDUtils.getTimestampInMicros;
public class QueueIndexUpdate {
public static final byte VALUE_CODE_BYTES = 0;
public static final byte VALUE_CODE_UTF8 = 1;
public static final byte VALUE_CODE_UUID = 2;
public static final byte VALUE_CODE_INT = 3;
public static final byte VALUE_CODE_MAX = 127;
public static int INDEX_STRING_VALUE_LENGTH = 1024;
private Mutator<ByteBuffer> batch;
private String queuePath;
private UUID queueId;
private String entryName;
private Object entryValue;
private final List<QueueIndexEntry> prevEntries = new ArrayList<QueueIndexEntry>();
private final List<QueueIndexEntry> newEntries = new ArrayList<QueueIndexEntry>();
private final Set<String> indexesSet = new LinkedHashSet<String>();
private long timestamp;
private final UUID timestampUuid;
public QueueIndexUpdate( Mutator<ByteBuffer> batch, String queuePath, UUID queueId, String entryName,
Object entryValue, UUID timestampUuid ) {
this.batch = batch;
this.queuePath = queuePath;
this.queueId = queueId;
this.entryName = entryName;
this.entryValue = entryValue;
timestamp = getTimestampInMicros( timestampUuid );
this.timestampUuid = timestampUuid;
}
public Mutator<ByteBuffer> getBatch() {
return batch;
}
public void setBatch( Mutator<ByteBuffer> batch ) {
this.batch = batch;
}
public String getQueuePath() {
return queuePath;
}
public void setQueuePath( String queuePath ) {
this.queuePath = queuePath;
}
public UUID getQueueId() {
return queueId;
}
public void setQueueId( UUID queueId ) {
this.queueId = queueId;
}
public String getEntryName() {
return entryName;
}
public void setEntryName( String entryName ) {
this.entryName = entryName;
}
public Object getEntryValue() {
return entryValue;
}
public void setEntryValue( Object entryValue ) {
this.entryValue = entryValue;
}
public long getTimestamp() {
return timestamp;
}
public void setTimestamp( long timestamp ) {
this.timestamp = timestamp;
}
public UUID getTimestampUuid() {
return timestampUuid;
}
public List<QueueIndexEntry> getPrevEntries() {
return prevEntries;
}
public void addPrevEntry( String path, Object value, UUID timestamp ) {
QueueIndexEntry entry = new QueueIndexEntry( path, value, timestamp );
prevEntries.add( entry );
}
public List<QueueIndexEntry> getNewEntries() {
return newEntries;
}
public void addNewEntry( String path, Object value ) {
QueueIndexEntry entry = new QueueIndexEntry( path, value, timestampUuid );
newEntries.add( entry );
}
public Set<String> getIndexesSet() {
return indexesSet;
}
public void addIndex( String index ) {
indexesSet.add( index );
}
public class QueueIndexEntry {
private final byte code;
private String path;
private final Object value;
private final UUID timestampUuid;
public QueueIndexEntry( String path, Object value, UUID timestampUuid ) {
this.path = path;
this.value = value;
code = indexValueCode( value );
this.timestampUuid = timestampUuid;
}
public String getPath() {
return path;
}
public void setPath( String path ) {
this.path = path;
}
public Object getValue() {
return value;
}
public byte getValueCode() {
return code;
}
public UUID getTimestampUuid() {
return timestampUuid;
}
public DynamicComposite getIndexComposite() {
return new DynamicComposite( code, value, getQueueId(), getQueuePath(), timestampUuid );
}
}
private static String prepStringForIndex( String str ) {
str = str.trim().toLowerCase();
str = str.substring( 0, Math.min( INDEX_STRING_VALUE_LENGTH, str.length() ) );
return str;
}
/**
* @param obj
* @return
*/
public static Object toIndexableValue( Object obj ) {
if ( obj == null ) {
return null;
}
if ( obj instanceof String ) {
return prepStringForIndex( ( String ) obj );
}
// UUIDs, and BigIntegers map to Cassandra UTF8Type and IntegerType
if ( ( obj instanceof UUID ) || ( obj instanceof BigInteger ) ) {
return obj;
}
// For any numeric values, turn them into a long
// and make them BigIntegers for IntegerType
if ( obj instanceof Number ) {
return BigInteger.valueOf( ( ( Number ) obj ).longValue() );
}
if ( obj instanceof Boolean ) {
return BigInteger.valueOf( ( ( Boolean ) obj ) ? 1L : 0L );
}
if ( obj instanceof Date ) {
return BigInteger.valueOf( ( ( Date ) obj ).getTime() );
}
if ( obj instanceof byte[] ) {
return wrap( ( byte[] ) obj );
}
if ( obj instanceof ByteBuffer ) {
return obj;
}
JsonNode json = toJsonNode( obj );
if ( ( json != null ) && json.isValueNode() ) {
if ( json.isBigInteger() ) {
return json.asInt();
//return json.getBigIntegerValue();
}
else if ( json.isNumber() || json.isBoolean() ) {
return BigInteger.valueOf( json.asLong() );
}
else if ( json.isTextual() ) {
return prepStringForIndex( json.asText() );
}
else if ( json.isBinary() ) {
try {
return wrap( json.binaryValue());
}
catch ( IOException e ) {
}
}
}
return null;
}
public static boolean validIndexableValue( Object obj ) {
return toIndexableValue( obj ) != null;
}
public static boolean validIndexableValueOrJson( Object obj ) {
if ( ( obj instanceof Map ) || ( obj instanceof List ) || ( obj instanceof JsonNode ) ) {
return true;
}
return toIndexableValue( obj ) != null;
}
public static byte indexValueCode( Object obj ) {
obj = toIndexableValue( obj );
if ( obj instanceof String ) {
return VALUE_CODE_UTF8;
}
else if ( obj instanceof UUID ) {
return VALUE_CODE_UUID;
}
else if ( obj instanceof BigInteger ) {
return VALUE_CODE_INT;
}
else if ( obj instanceof Number ) {
return VALUE_CODE_INT;
}
else {
return VALUE_CODE_BYTES;
}
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static int compareIndexedValues( Object o1, Object o2 ) {
o1 = toIndexableValue( o1 );
o2 = toIndexableValue( o2 );
if ( ( o1 == null ) && ( o2 == null ) ) {
return 0;
}
else if ( o1 == null ) {
return -1;
}
else if ( o2 == null ) {
return 1;
}
int c1 = indexValueCode( o1 );
int c2 = indexValueCode( o2 );
if ( c1 == c2 ) {
if ( o1 instanceof UUID ) {
UUIDComparator.staticCompare( ( UUID ) o1, ( UUID ) o2 );
}
else if ( o1 instanceof Comparable ) {
return ( ( Comparable ) o1 ).compareTo( o2 );
}
}
return c1 - c2;
}
}