| // 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.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.google.gson.JsonArray; | |
| import com.google.gson.JsonElement; | |
| import com.google.gson.JsonObject; | |
| import com.google.gson.JsonPrimitive; | |
| import com.cloud.bridge.service.exception.InternalErrorException; | |
| /** | |
| * 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 | |
| * | |
| */ | |
| 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()); | |
| } | |
| } | |
| } |