blob: 9de3d9d2ac735e97e7140da7a7e538db41a7a8fa [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 org.apache.s2graph.core
import org.apache.s2graph.core.GraphExceptions.IllegalDataTypeException
import org.apache.s2graph.core.mysqls.LabelMeta
import org.apache.s2graph.core.rest.TemplateHelper
import org.apache.s2graph.core.types.{InnerVal, InnerValLike, InnerValLikeWithTs, VertexId}
import org.apache.s2graph.core.utils.logger
import play.api.libs.json._
object JSONParser {
//TODO: check result notation on bigDecimal.
def innerValToJsValue(innerVal: InnerValLike, dataType: String): Option[JsValue] = {
try {
val dType = InnerVal.toInnerDataType(dataType)
val jsValue = dType match {
case InnerVal.STRING => JsString(innerVal.value.asInstanceOf[String])
case InnerVal.BOOLEAN => JsBoolean(innerVal.value.asInstanceOf[Boolean])
case InnerVal.BYTE | InnerVal.SHORT | InnerVal.INT | InnerVal.LONG | InnerVal.FLOAT | InnerVal.DOUBLE =>
// case t if InnerVal.NUMERICS.contains(t) =>
innerVal.value match {
case l: Long => JsNumber(l)
case i: Int => JsNumber(i)
case s: Short => JsNumber(s.toLong)
case b: Byte => JsNumber(b.toLong)
case f: Float => JsNumber(f.toDouble)
case d: Double =>
// JsNumber(d)
dType match {
case InnerVal.BYTE => JsNumber(d.toInt)
case InnerVal.SHORT => JsNumber(d.toInt)
case InnerVal.INT => JsNumber(d.toInt)
case InnerVal.LONG => JsNumber(d.toLong)
case InnerVal.FLOAT => JsNumber(d.toDouble)
case InnerVal.DOUBLE => JsNumber(d.toDouble)
case _ => throw new RuntimeException(s"$innerVal, $dType => $dataType")
}
case num: BigDecimal =>
// JsNumber(num)
// JsNumber(InnerVal.scaleNumber(num.asInstanceOf[BigDecimal], dType))
dType match {
case InnerVal.BYTE => JsNumber(num.toInt)
case InnerVal.SHORT => JsNumber(num.toInt)
case InnerVal.INT => JsNumber(num.toInt)
case InnerVal.LONG => JsNumber(num.toLong)
case InnerVal.FLOAT => JsNumber(num.toDouble)
case InnerVal.DOUBLE => JsNumber(num.toDouble)
case _ => throw new RuntimeException(s"$innerVal, $dType => $dataType")
}
// JsNumber(num.toLong)
case _ => throw new RuntimeException(s"$innerVal, Numeric Unknown => $dataType")
}
// JsNumber(InnerVal.scaleNumber(innerVal.asInstanceOf[BigDecimal], dType))
case _ => throw new RuntimeException(s"$innerVal, Unknown => $dataType")
}
Some(jsValue)
} catch {
case e: Exception =>
logger.info(s"JSONParser.innerValToJsValue: $e")
None
}
}
// def innerValToString(innerVal: InnerValLike, dataType: String): String = {
// val dType = InnerVal.toInnerDataType(dataType)
// InnerVal.toInnerDataType(dType) match {
// case InnerVal.STRING => innerVal.toString
// case InnerVal.BOOLEAN => innerVal.toString
// // case t if InnerVal.NUMERICS.contains(t) =>
// case InnerVal.BYTE | InnerVal.SHORT | InnerVal.INT | InnerVal.LONG | InnerVal.FLOAT | InnerVal.DOUBLE =>
// BigDecimal(innerVal.toString).bigDecimal.toPlainString
// case _ => innerVal.toString
// // throw new RuntimeException("innerVal to jsValue failed.")
// }
// }
// def toInnerVal(str: String, dataType: String, version: String): InnerValLike = {
// //TODO:
// // logger.error(s"toInnerVal: $str, $dataType, $version")
// val s =
// if (str.startsWith("\"") && str.endsWith("\"")) str.substring(1, str.length - 1)
// else str
// val dType = InnerVal.toInnerDataType(dataType)
//
// dType match {
// case InnerVal.STRING => InnerVal.withStr(s, version)
// // case t if InnerVal.NUMERICS.contains(t) => InnerVal.withNumber(BigDecimal(s), version)
// case InnerVal.BYTE | InnerVal.SHORT | InnerVal.INT | InnerVal.LONG | InnerVal.FLOAT | InnerVal.DOUBLE =>
// InnerVal.withNumber(BigDecimal(s), version)
// case InnerVal.BOOLEAN => InnerVal.withBoolean(s.toBoolean, version)
// case InnerVal.BLOB => InnerVal.withBlob(s.getBytes, version)
// case _ =>
// // InnerVal.withStr("")
// throw new RuntimeException(s"illegal datatype for string: dataType is $dataType for $s")
// }
// }
def isNumericType(dType: String): Boolean = {
dType == InnerVal.LONG || dType == InnerVal.INT ||
dType == InnerVal.SHORT || dType == InnerVal.BYTE ||
dType == InnerVal.FLOAT || dType == InnerVal.DOUBLE
}
//TODO: fix this messy parts
def innerValToAny(innerValLike: InnerValLike, dataType: String): Any = {
val dType = InnerVal.toInnerDataType(dataType)
dType match {
case InnerVal.LONG =>
innerValLike.value match {
case b: BigDecimal => b.toLong
case l: Long => l
case i: Int => i.toLong
case f: Float => f.toLong
case d: Double => d.toLong
case _ => throw new RuntimeException(s"not supported data type: $innerValLike, ${innerValLike.value.getClass}, $dataType")
}
case InnerVal.INT =>
innerValLike.value match {
case b: BigDecimal => b.toInt
case l: Long => l.toInt
case i: Int => i
case f: Float => f.toInt
case d: Double => d.toInt
case _ => throw new RuntimeException(s"not supported data type: $innerValLike, ${innerValLike.value.getClass}, $dataType")
}
case InnerVal.SHORT =>
innerValLike.value match {
case b: BigDecimal => b.toShort
case s: Short => s
case _ => throw new RuntimeException(s"not supported data type: $innerValLike, ${innerValLike.value.getClass}, $dataType")
}
case InnerVal.BYTE =>
innerValLike.value match {
case b: BigDecimal => b.toByte
case b: Byte => b
case _ => throw new RuntimeException(s"not supported data type: $innerValLike, ${innerValLike.value.getClass}, $dataType")
}
case InnerVal.FLOAT =>
innerValLike.value match {
case b: BigDecimal => b.toFloat
case d: Double => d.toFloat
case f: Float => f
case l: Long => l.toFloat
case i: Int => i.toFloat
case _ => throw new RuntimeException(s"not supported data type: $innerValLike, ${innerValLike.value.getClass}, $dataType")
}
case InnerVal.DOUBLE =>
innerValLike.value match {
case b: BigDecimal => b.toDouble
case d: Double => d
case l: Long => l.toDouble
case i: Int => i.toDouble
case f: Float => f.toDouble
case _ => throw new RuntimeException(s"not supported data type: $innerValLike, ${innerValLike.value.getClass}, $dataType")
}
case _ => innerValLike.value
}
}
def toInnerVal(any: Any, dataType: String, version: String): InnerValLike = {
val dType = InnerVal.toInnerDataType(dataType)
val isNumeric = isNumericType(dType)
any match {
case v: VertexId => v.innerId
case a: InnerValLike => a
case n: BigDecimal =>
if (isNumeric) InnerVal.withNumber(n, version)
else if (dType == InnerVal.STRING) InnerVal.withStr(n.toString, version)
else throw new IllegalDataTypeException(s"[ValueType] = BigDecimal, [DataType]: $dataType, [Input]: $any")
case l: Long =>
if (isNumeric) InnerVal.withLong(l, version)
else if (dType == InnerVal.STRING) InnerVal.withStr(l.toString, version)
else throw new IllegalDataTypeException(s"[ValueType] = Long, [DataType]: $dataType, [Input]: $any")
case i: Int =>
if (isNumeric) InnerVal.withInt(i, version)
else if (dType == InnerVal.STRING) InnerVal.withStr(i.toString, version)
else throw new IllegalDataTypeException(s"[ValueType] = Int, [DataType]: $dataType, [Input]: $any")
case sh: Short =>
if (isNumeric) InnerVal.withInt(sh.toInt, version)
else if (dType == InnerVal.STRING) InnerVal.withStr(sh.toString, version)
else throw new IllegalDataTypeException(s"[ValueType] = Short, [DataType]: $dataType, [Input]: $any")
case b: Byte =>
if (isNumeric) InnerVal.withInt(b.toInt, version)
else if (dType == InnerVal.STRING) InnerVal.withStr(b.toString, version)
else throw new IllegalDataTypeException(s"[ValueType] = Byte, [DataType]: $dataType, [Input]: $any")
case f: Float =>
if (isNumeric) InnerVal.withFloat(f, version)
else if (dType == InnerVal.STRING) InnerVal.withStr(f.toString, version)
else throw new IllegalDataTypeException(s"[ValueType] = Float, [DataType]: $dataType, [Input]: $any")
case d: Double =>
if (isNumeric) InnerVal.withDouble(d, version)
else if (dType == InnerVal.STRING) InnerVal.withStr(d.toString, version)
else throw new IllegalDataTypeException(s"[ValueType] = Double, [DataType]: $dataType, [Input]: $any")
case bl: Boolean =>
if (dType == InnerVal.BOOLEAN) InnerVal.withBoolean(bl, version)
else if (dType == InnerVal.STRING) InnerVal.withStr(bl.toString, version)
else throw new IllegalDataTypeException(s"[ValueType] = Boolean, [DataType]: $dataType, [Input]: $any")
case _s: String =>
if (isNumeric) {
try {
val s = TemplateHelper.replaceVariable(System.currentTimeMillis(), _s.toString)
InnerVal.withNumber(BigDecimal(s), version)
} catch {
case e: Exception =>
throw new IllegalDataTypeException(s"[ValueType] = String, [DataType]: $dataType, [Input]: $any")
}
} else {
dType match {
case InnerVal.BOOLEAN => try {
InnerVal.withBoolean(_s.toBoolean, version)
} catch {
case e: Exception =>
throw new IllegalDataTypeException(s"[ValueType] = String, [DataType]: boolean, [Input]: $any")
}
case InnerVal.STRING => InnerVal.withStr(_s, version)
}
}
}
}
def jsValueToInnerVal(jsValue: JsValue, dataType: String, version: String): Option[InnerValLike] = {
val ret = try {
val dType = InnerVal.toInnerDataType(dataType.toLowerCase())
jsValue match {
case n: JsNumber =>
dType match {
case InnerVal.STRING => Some(InnerVal.withStr(jsValue.toString, version))
// case t if InnerVal.NUMERICS.contains(t) =>
case InnerVal.BYTE | InnerVal.SHORT | InnerVal.INT | InnerVal.LONG | InnerVal.FLOAT | InnerVal.DOUBLE =>
Some(InnerVal.withNumber(n.value, version))
case _ => None
}
case _s: JsString =>
val s = TemplateHelper.replaceVariable(System.currentTimeMillis(), _s.value)
dType match {
case InnerVal.STRING => Some(InnerVal.withStr(s, version))
case InnerVal.BOOLEAN => Some(InnerVal.withBoolean(s.toBoolean, version))
// case t if InnerVal.NUMERICS.contains(t) =>
case InnerVal.BYTE | InnerVal.SHORT | InnerVal.INT | InnerVal.LONG | InnerVal.FLOAT | InnerVal.DOUBLE =>
Some(InnerVal.withNumber(BigDecimal(s), version))
case _ => None
}
case b: JsBoolean =>
dType match {
case InnerVal.STRING => Some(InnerVal.withStr(b.toString, version))
case InnerVal.BOOLEAN => Some(InnerVal.withBoolean(b.value, version))
case _ => None
}
case _ =>
None
}
} catch {
case e: Exception =>
logger.error(s"jsValueToInnerVal: jsValue = ${jsValue}, dataType = ${dataType}, version = ${version}", e)
None
}
ret
}
def anyValToJsValue(value: Any): Option[JsValue] = {
try {
val v = value match {
case null => JsNull
case l: Long => JsNumber(l)
case i: Int => JsNumber(i)
case s: Short => JsNumber(s.toInt)
case b: Byte => JsNumber(b.toInt)
case f: Float => JsNumber(f.toDouble)
case d: Double => JsNumber(d)
case bd: BigDecimal => if (bd.isValidLong) JsNumber(bd.toLong) else JsNumber(bd)
case s: String => JsString(s)
case b: Boolean => JsBoolean(b)
case _ => throw new RuntimeException(s"$value, ${value.getClass.getName} is not supported data type.")
}
Option(v)
} catch {
case e: Exception =>
logger.error(s"anyValToJsValue: $value", e)
None
}
}
def jsValueToAny(value: JsValue): Option[AnyRef] = {
try {
value match {
case n: JsNumber => Option(n.value)
case s: JsString => Option(TemplateHelper.replaceVariable(System.currentTimeMillis(), s.value))
case b: JsBoolean => Option(Boolean.box(b.value))
case _ => None
}
} catch {
case e: Exception =>
logger.error(s"jsValueToAny: $value", e)
None
}
}
def propertiesToJson(props: Map[String, Any],
selectColumns: Map[String, Boolean] = Map.empty): Map[String, JsValue] = {
if (selectColumns.isEmpty) {
for {
(k, v) <- props
jsValue <- anyValToJsValue(v)
// labelMeta <- label.metaPropsInvMap.get(k)
// innerVal = toInnerVal(v.toString, labelMeta.dataType, labelMeta.)
} yield {
k -> jsValue
}
} else {
for {
(k, _) <- selectColumns
v <- props.get(k)
jsValue <- anyValToJsValue(v)
} yield k -> jsValue
}
}
def jsValueToString(jsValue: JsValue): String = {
jsValue match {
case s: JsString => s.value
case _ => jsValue.toString
}
}
def fromJsonToProperties(jsObject: JsObject): Map[String, Any] = {
val kvs = for {
(k, v) <- jsObject.fieldSet
} yield {
k -> jsValueToString(v)
}
kvs.toMap
}
}