blob: 1aa4eca9e830de5f762b9fa68a0c9f9e106199a4 [file] [log] [blame]
/**
* 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 com.jwplayer.sqe.language.expression;
import com.google.gson.*;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
public abstract class BaseExpression {
public BaseExpression() { }
public abstract ExpressionType getExpressionType();
public abstract String getOutputFieldName();
public abstract List<BaseExpression> unRoll();
public static class BaseExpressionTypeAdapter implements JsonDeserializer<BaseExpression>, JsonSerializer<BaseExpression> {
@Override
public BaseExpression deserialize(JsonElement jsonElement, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
if(jsonElement.isJsonPrimitive()) {
JsonPrimitive primitive = jsonElement.getAsJsonPrimitive();
if(primitive.isString()) return new FieldExpression(jsonElement.getAsJsonPrimitive().getAsString());
else return parseConstant(primitive);
} else if(jsonElement.isJsonObject()) {
Set<Map.Entry<String, JsonElement>> entrySet = jsonElement.getAsJsonObject().entrySet();
if (entrySet.size() != 1) {
throw new RuntimeException("The following JSON should contain one and only one element:\n" + jsonElement.getAsString());
}
for (Map.Entry<String, JsonElement> entry : entrySet) {
if (entry.getKey().equals("C")) {
if(entry.getValue().isJsonNull()) return new ConstantExpression(null);
else return parseConstant(entry.getValue().getAsJsonPrimitive());
} else {
List<BaseExpression> arguments = deserializeArguments(entry.getValue().getAsJsonArray(),
type, jsonDeserializationContext);
return FunctionExpression.makeFunction(entry.getKey(), arguments);
}
}
} else if(jsonElement.isJsonNull()) {
return new ConstantExpression(null);
}
throw new JsonParseException("Could not parse json:\n" + jsonElement.getAsString());
}
private List<BaseExpression> deserializeArguments(JsonArray array, Type type, JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
List<BaseExpression> arguments = new ArrayList<>();
for(JsonElement jsonElement: array) {
arguments.add(deserialize(jsonElement, type, jsonDeserializationContext));
}
return arguments;
}
private ConstantExpression parseConstant(JsonPrimitive primitive) {
if(primitive.isBoolean()) return new ConstantExpression(primitive.getAsBoolean());
else if(primitive.isNumber()) {
// This is to prevent numerical constants from being parsed as BigDecimal, instead opting
// for something slightly more sane and workable.
// FIXME: This doesn't work in cultures where . is the 1000s separator
if(primitive.getAsString().contains("."))
return new ConstantExpression(primitive.getAsNumber().doubleValue());
else {
long longValue = primitive.getAsNumber().longValue();
if(longValue < Integer.MAX_VALUE)
return new ConstantExpression(primitive.getAsNumber().intValue());
else
return new ConstantExpression(primitive.getAsNumber().longValue());
}
}
else if(primitive.isString()) return new ConstantExpression(primitive.getAsString());
else throw new JsonParseException("Invalid JSON primitive:\n" + primitive.getAsString());
}
@Override
public JsonElement serialize(BaseExpression expression, Type type, JsonSerializationContext jsonSerializationContext) {
JsonObject object = new JsonObject();
switch(expression.getExpressionType()) {
case Constant:
ConstantExpression constant = (ConstantExpression) expression;
if(constant.constant == null) return JsonNull.INSTANCE;
else if(constant.constant instanceof Boolean) return new JsonPrimitive((Boolean) constant.constant);
else if(constant.constant instanceof Number) return new JsonPrimitive((Number) constant.constant);
else if(constant.constant instanceof String) object.addProperty("C", (String) constant.constant);
else throw new RuntimeException(constant.constant.getClass().getSimpleName() + " is not a valid constant type");
return object;
case Field:
return new JsonPrimitive(((FieldExpression) expression).fieldName);
case Function:
FunctionExpression function = (FunctionExpression) expression;
JsonArray jsonArray = new JsonArray();
for(BaseExpression argument: function.getArguments()) {
jsonArray.add(serialize(argument, type, jsonSerializationContext));
}
object.add(function.getFunctionName(), jsonArray);
return object;
default:
throw new RuntimeException(expression.getExpressionType().toString() + " is not a serializable expression type");
}
}
}
}