/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.elasticsearch.test.rest.yaml;
import org.apache.http.util.EntityUtils;
import org.elasticsearch.client.Response;
import org.elasticsearch.common.bytes.BytesArray;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Holds an object and allows to extract specific values from it given their path
*/
public class ObjectPath {
private final Object object;
public static ObjectPath createFromResponse(Response response) throws IOException {
byte[] bytes = EntityUtils.toByteArray(response.getEntity());
String contentType = response.getHeader("Content-Type");
XContentType xContentType = XContentType.fromMediaTypeOrFormat(contentType);
return ObjectPath.createFromXContent(xContentType.xContent(), new BytesArray(bytes));
}
public static ObjectPath createFromXContent(XContent xContent, BytesReference input) throws IOException {
try (XContentParser parser = xContent.createParser(NamedXContentRegistry.EMPTY, input)) {
if (parser.nextToken() == XContentParser.Token.START_ARRAY) {
return new ObjectPath(parser.listOrderedMap());
}
return new ObjectPath(parser.mapOrdered());
}
}
public ObjectPath(Object object) {
this.object = object;
}
/**
* A utility method that creates an {@link ObjectPath} via {@link #ObjectPath(Object)} returns
* the result of calling {@link #evaluate(String)} on it.
*/
public static <T> T evaluate(Object object, String path) throws IOException {
return new ObjectPath(object).evaluate(path, Stash.EMPTY);
}
/**
* Returns the object corresponding to the provided path if present, null otherwise
*/
public <T> T evaluate(String path) throws IOException {
return evaluate(path, Stash.EMPTY);
}
/**
* Returns the object corresponding to the provided path if present, null otherwise
*/
@SuppressWarnings("unchecked")
public <T> T evaluate(String path, Stash stash) throws IOException {
String[] parts = parsePath(path);
Object object = this.object;
for (String part : parts) {
object = evaluate(part, object, stash);
if (object == null) {
return null;
}
}
return (T)object;
}
@SuppressWarnings("unchecked")
private Object evaluate(String key, Object object, Stash stash) throws IOException {
if (stash.containsStashedValue(key)) {
key = stash.getValue(key).toString();
}
if (object instanceof Map) {
return ((Map<String, Object>) object).get(key);
}
if (object instanceof List) {
List<Object> list = (List<Object>) object;
try {
return list.get(Integer.valueOf(key));
} catch (NumberFormatException e) {
throw new IllegalArgumentException("element was a list, but [" + key + "] was not numeric", e);
} catch (IndexOutOfBoundsException e) {
throw new IllegalArgumentException("element was a list with " + list.size() +
" elements, but [" + key + "] was out of bounds", e);
}
}
throw new IllegalArgumentException("no object found for [" + key + "] within object of class [" + object.getClass() + "]");
}
private String[] parsePath(String path) {
List<String> list = new ArrayList<>();
StringBuilder current = new StringBuilder();
boolean escape = false;
for (int i = 0; i < path.length(); i++) {
char c = path.charAt(i);
if (c == '\\') {
escape = true;
continue;
}
if (c == '.') {
if (escape) {
escape = false;
} else {
if (current.length() > 0) {
list.add(current.toString());
current.setLength(0);
}
continue;
}
}
current.append(c);
}
if (current.length() > 0) {
list.add(current.toString());
}
return list.toArray(new String[list.size()]);
}
}