/*
* Copyright (C) 2011 Citrix Systems, Inc. All rights reserved.
*
* 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.
*/
package com.cloud.bridge.util;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import com.cloud.bridge.service.exception.InternalErrorException;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonPrimitive;
/**
* JsonAccessor provides the functionality to allow navigating JSON object graph using simple expressions,
* for example, following property access expressions are all valid ones
*
* rootobj.level1obj[1].property
* this[0].level1obj[1].property
*
* @author Kelven Yang
*/
public class JsonAccessor {
private JsonElement _json;
Pattern _arrayAccessorMatcher = Pattern.compile("(.*)\\[(\\d+)\\]");
public JsonAccessor(JsonElement json) {
assert(json != null);
_json = json;
}
public BigDecimal getAsBigDecimal(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsBigDecimal();
}
public BigInteger getAsBigInteger(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsBigInteger();
}
public boolean getAsBoolean(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsBoolean();
}
public byte getAsByte(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsByte();
}
public char getAsCharacter(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsCharacter();
}
public double getAsDouble(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsDouble();
}
public float getAsFloat(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsFloat();
}
public int getAsInt(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsInt();
}
public long getAsLong(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsLong();
}
public Number getAsNumber(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsNumber();
}
public short getAsShort(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsShort();
}
public String getAsString(String propPath) {
JsonElement jsonElement = eval(propPath);
return jsonElement.getAsString();
}
public boolean isBoolean(String propPath) {
JsonElement jsonElement = eval(propPath);
if(jsonElement instanceof JsonPrimitive)
return ((JsonPrimitive)jsonElement).isBoolean();
return false;
}
public boolean isNumber(String propPath) {
JsonElement jsonElement = eval(propPath);
if(jsonElement instanceof JsonPrimitive)
return ((JsonPrimitive)jsonElement).isNumber();
return false;
}
public boolean isString(String propPath) {
JsonElement jsonElement = eval(propPath);
if(jsonElement instanceof JsonPrimitive)
return ((JsonPrimitive)jsonElement).isString();
return false;
}
/*
* Return
* -1 : property expression can not be resolved
* 0 : match to a null JSON object
* 1+ : matched, for array element, the count of the elements inside the array
*/
public int getMatchCount(String propPath) {
JsonElement jsonElement = tryEval(propPath);
if(jsonElement == null)
return -1;
if(jsonElement.isJsonNull())
return 0;
if(jsonElement.isJsonArray())
return ((JsonArray)jsonElement).size();
return 1;
}
public JsonElement eval(String propPath) {
JsonElement jsonElement = tryEval(propPath);
if(jsonElement == null)
throw new InternalErrorException("Property " + propPath + " is resolved to null JSON element on object: " + _json.toString());
return jsonElement;
}
public JsonElement tryEval(String propPath) {
assert(propPath != null);
String[] tokens = propPath.split("\\.");
ArrayList<Resolver> resolverChain = new ArrayList<Resolver>();
for(String token : tokens) {
Matcher matcher = _arrayAccessorMatcher.matcher(token);
if(matcher.find()) {
String propStr = matcher.group(1);
String indexStr = matcher.group(2);
resolverChain.add(new ArrayPropertyResolver(propStr, Integer.parseInt(indexStr)));
} else {
resolverChain.add(new PropertyResolver(token));
}
}
JsonElement jsonElementToResolveAt = _json;
for(Resolver resolver : resolverChain) {
jsonElementToResolveAt = resolver.resolve(jsonElementToResolveAt);
if(jsonElementToResolveAt == null)
break;
}
return jsonElementToResolveAt;
}
//
// Property resolvers
//
private static interface Resolver {
public JsonElement resolve(JsonElement jsonElementToResolveAt);
}
private static class PropertyResolver implements Resolver {
protected String _propName;
public PropertyResolver(String propName) {
_propName = propName;
}
public JsonElement resolve(JsonElement jsonElementToResolveAt) {
if("this".equals(_propName))
return jsonElementToResolveAt;
if(jsonElementToResolveAt.isJsonObject())
return ((JsonObject)jsonElementToResolveAt).get(_propName);
if(jsonElementToResolveAt.isJsonNull())
throw new NullPointerException(String.format("Property %s points to a null element on object: %s", _propName, jsonElementToResolveAt.toString()));
throw new InternalErrorException("Unable to evaluate JSON accessor property: " + _propName
+ ", on object: " + jsonElementToResolveAt.toString());
}
}
private static class ArrayPropertyResolver extends PropertyResolver {
protected int _index;
public ArrayPropertyResolver(String propName, int index) {
super(propName);
_index = index;
}
public JsonElement resolve(JsonElement jsonElementToResolveAt) {
if(!"this".equals(_propName)) {
if(jsonElementToResolveAt.isJsonObject()) {
jsonElementToResolveAt = ((JsonObject)jsonElementToResolveAt).get(_propName);
} else {
if(jsonElementToResolveAt.isJsonNull())
throw new NullPointerException(String.format("Property %s points to a null element on object: %s", _propName, jsonElementToResolveAt.toString()));
throw new InternalErrorException("Unable to evaluate JSON accessor property: " + _propName
+ ", on object: " + jsonElementToResolveAt.toString());
}
}
if(jsonElementToResolveAt instanceof JsonArray) {
return ((JsonArray)jsonElementToResolveAt).get(_index);
}
if(jsonElementToResolveAt.isJsonNull())
throw new NullPointerException(String.format("Property %s points to a null element on object: %s", _propName, jsonElementToResolveAt.toString()));
throw new InternalErrorException("Unable to evaluate JSON accessor property: " + _propName
+ ", on object: " + jsonElementToResolveAt.toString());
}
}
}