| /* | |
| * 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()); | |
| } | |
| } | |
| } |