/*
* Copyright 1999-2012 Alibaba Group.
*
* 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.
*/
/**
* (created at 2011-8-11)
*/
package com.alibaba.cobar.route.function;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.alibaba.cobar.config.model.rule.RuleAlgorithm;
import com.alibaba.cobar.parser.ast.expression.Expression;
import com.alibaba.cobar.parser.ast.expression.primary.function.FunctionExpression;
import com.alibaba.cobar.parser.util.Pair;
import com.alibaba.cobar.parser.util.PairUtil;
import com.alibaba.cobar.route.util.PartitionUtil;
import com.alibaba.cobar.util.SplitUtil;
import com.alibaba.cobar.util.StringUtil;
/**
* @author <a href="mailto:shuo.qius@alibaba-inc.com">QIU Shuo</a>
*/
public final class Dimension2PartitionFunction extends FunctionExpression implements RuleAlgorithm {
public Dimension2PartitionFunction(String functionName) {
this(functionName, null);
}
public Dimension2PartitionFunction(String functionName, List<Expression> arguments) {
super(functionName, arguments);
}
private static final int PARTITION_KEY_TYPE_LONG = 1;
private static final int PARTITION_KEY_TYPE_STRING = 2;
private int[] countX;
private int[] lengthX;
private int[] countY;
private int[] lengthY;
private static int convertType(String keyType) {
if ("long".equalsIgnoreCase(keyType))
return PARTITION_KEY_TYPE_LONG;
if ("string".equalsIgnoreCase(keyType))
return PARTITION_KEY_TYPE_STRING;
throw new IllegalArgumentException("unknown partition key type: " + keyType);
}
public void setKeyTypeX(String keyTypeX) {
this.keyTypeX = convertType(keyTypeX);
}
public void setKeyTypeY(String keyTypeY) {
this.keyTypeY = convertType(keyTypeY);
}
public void setPartitionCountX(String partitionCount) {
this.countX = toIntArray(partitionCount);
this.xSize = 0;
for (int c : countX)
this.xSize += c;
}
public void setPartitionLengthX(String partitionLength) {
this.lengthX = toIntArray(partitionLength);
}
public void setHashLengthX(int hashLengthX) {
setHashSliceX(String.valueOf(hashLengthX));
}
public void setHashSliceX(String hashSlice) {
Pair<Integer, Integer> p = PairUtil.sequenceSlicing(hashSlice);
hashSliceStartX = p.getKey();
hashSliceEndX = p.getValue();
}
public void setPartitionCountY(String partitionCount) {
this.countY = toIntArray(partitionCount);
this.ySize = 0;
for (int c : countY)
this.ySize += c;
}
public void setPartitionLengthY(String partitionLength) {
this.lengthY = toIntArray(partitionLength);
}
public void setHashLengthY(int hashLengthY) {
setHashSliceY(String.valueOf(hashLengthY));
}
public void setHashSliceY(String hashSlice) {
Pair<Integer, Integer> p = PairUtil.sequenceSlicing(hashSlice);
hashSliceStartY = p.getKey();
hashSliceEndY = p.getValue();
}
private static int[] toIntArray(String string) {
String[] strs = SplitUtil.split(string, ',', true);
int[] ints = new int[strs.length];
for (int i = 0; i < strs.length; ++i) {
ints[i] = Integer.parseInt(strs[i]);
}
return ints;
}
private int xSize;
private int keyTypeX = PARTITION_KEY_TYPE_LONG;
private int hashSliceStartX = 0;
private int hashSliceEndX = 8;
private PartitionUtil partitionUtilX;
private int ySize;
private int keyTypeY = PARTITION_KEY_TYPE_LONG;
private int hashSliceStartY = 0;
private int hashSliceEndY = 8;
private PartitionUtil partitionUtilY;
private Integer[][] byX;
private Integer[][] byY;
private Integer[] all;
private void buildByX() {
byX = new Integer[xSize][ySize];
for (int x = 0; x < xSize; ++x) {
for (int y = 0; y < ySize; ++y) {
byX[x][y] = getByXY(x, y);
}
}
}
private void buildByY() {
byY = new Integer[ySize][xSize];
for (int y = 0; y < ySize; ++y) {
for (int x = 0; x < xSize; ++x) {
byY[y][x] = getByXY(x, y);
}
}
}
private void buildAll() {
int size = xSize * ySize;
all = new Integer[size];
for (int i = 0; i < size; ++i)
all[i] = i;
}
private Integer[] getAll() {
return all;
}
private Integer[] getByX(final int x) {
return byX[x];
}
private Integer[] getByY(final int y) {
return byY[y];
}
private Integer getByXY(int x, int y) {
if (x >= xSize || y >= ySize)
throw new IllegalArgumentException("x, y out of bound: x=" + x + ", y=" + y);
return x + xSize * y;
}
/**
* @return null if eval invalid type
*/
private static Integer calculate(Object eval, PartitionUtil partitionUtil, int keyType, int hashSliceStart,
int hashSliceEnd) {
if (eval == UNEVALUATABLE || eval == null)
return null;
switch (keyType) {
case PARTITION_KEY_TYPE_LONG:
long longVal;
if (eval instanceof Number) {
longVal = ((Number) eval).longValue();
} else if (eval instanceof String) {
longVal = Long.parseLong((String) eval);
} else {
throw new IllegalArgumentException("unsupported data type for partition key: " + eval.getClass());
}
return partitionUtil.partition(longVal);
case PARTITION_KEY_TYPE_STRING:
String key = String.valueOf(eval);
int start = hashSliceStart >= 0 ? hashSliceStart : key.length() + hashSliceStart;
int end = hashSliceEnd > 0 ? hashSliceEnd : key.length() + hashSliceEnd;
long hash = StringUtil.hash(key, start, end);
return partitionUtil.partition(hash);
default:
throw new IllegalArgumentException("unsupported partition key type: " + keyType);
}
}
@Override
public Integer[] evaluationInternal(Map<? extends Object, ? extends Object> parameters) {
return calculate(parameters);
}
private Integer[] eval(Object xInput, Object yInput) {
Integer x = calculate(xInput, partitionUtilX, keyTypeX, hashSliceStartX, hashSliceEndX);
Integer y = calculate(yInput, partitionUtilY, keyTypeY, hashSliceStartY, hashSliceEndY);
if (x != null && y != null) {
return new Integer[] { getByXY(x, y) };
} else if (x == null && y != null) {
return getByY(y);
} else if (x != null && y == null) {
return getByX(x);
} else {
return getAll();
}
}
@Override
public void init() {
initialize();
}
@Override
public void initialize() {
partitionUtilX = new PartitionUtil(countX, lengthX);
partitionUtilY = new PartitionUtil(countY, lengthY);
buildAll();
buildByX();
buildByY();
}
@Override
public FunctionExpression constructFunction(List<Expression> arguments) {
if (arguments == null || arguments.size() != 2)
throw new IllegalArgumentException("function " + getFunctionName() + " must have 2 arguments but is "
+ arguments);
Object[] args = new Object[arguments.size()];
int i = -1;
for (Expression arg : arguments) {
args[++i] = arg;
}
return (FunctionExpression) constructMe(args);
}
@Override
public RuleAlgorithm constructMe(Object... objects) {
List<Expression> args = new ArrayList<Expression>(objects.length);
for (Object obj : objects) {
args.add((Expression) obj);
}
Dimension2PartitionFunction rst = new Dimension2PartitionFunction(functionName, args);
rst.countX = countX;
rst.xSize = xSize;
rst.lengthX = lengthX;
rst.keyTypeX = keyTypeX;
rst.hashSliceStartX = hashSliceStartX;
rst.hashSliceEndX = hashSliceEndX;
rst.countY = countY;
rst.ySize = ySize;
rst.lengthY = lengthY;
rst.keyTypeY = keyTypeY;
rst.hashSliceStartY = hashSliceStartY;
rst.hashSliceEndY = hashSliceEndY;
return rst;
}
@Override
public Integer[] calculate(Map<? extends Object, ? extends Object> parameters) {
if (arguments == null || arguments.size() < 2)
throw new IllegalArgumentException("arguments.size < 2 for function of " + getFunctionName());
Object xInput = arguments.get(0).evaluation(parameters);
Object yInput = arguments.get(1).evaluation(parameters);
return eval(xInput, yInput);
}
// public static void main(String[] args) throws Exception {
// Dimension2PartitionFunction func = new
// Dimension2PartitionFunction("test999", new ArrayList<Expression>(2));
// func.setKeyTypeX("long");
// func.setPartitionCountX("1,2");
// func.setPartitionLengthX("512,256");
// func.setKeyTypeY("string");
// func.setPartitionCountY("2");
// func.setPartitionLengthY("512");
// func.setHashLengthY(8);
// func.init();
//
// Integer[] ints=func.eval(1023L, "zzzz");
// for(Integer i:ints){
// System.out.println(i);
// }
// }
}