/*
* Copyright 2014 the original author or authors.
*
* 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 org.springframework.sync;
import java.lang.reflect.Array;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* Utilities for converting patch paths to/from SpEL expressions.
*
* For example, "/foo/bars/1/baz" becomes "foo.bars[1].baz".
*
* @author Craig Walls
*/
public class PathToSpEL {
private static final SpelExpressionParser SPEL_EXPRESSION_PARSER = new SpelExpressionParser();
/**
* Converts a patch path to an {@link Expression}.
* @param path the patch path to convert.
* @return an {@link Expression}
*/
public static Expression pathToExpression(String path) {
return SPEL_EXPRESSION_PARSER.parseExpression(pathToSpEL(path));
}
/**
* Convenience method to convert a SpEL String to an {@link Expression}.
* @param spel the SpEL expression as a String
* @return an {@link Expression}
*/
public static Expression spelToExpression(String spel) {
return SPEL_EXPRESSION_PARSER.parseExpression(spel);
}
/**
* Produces an expression targeting the parent of the object that the given path targets.
* @param path the path to find a parent expression for.
* @return an {@link Expression} targeting the parent of the object specifed by path.
*/
public static Expression pathToParentExpression(String path) {
return spelToExpression(pathNodesToSpEL(copyOf(path.split("\\/"), path.split("\\/").length - 1)));
}
// private helpers
private static String pathToSpEL(String path) {
return pathNodesToSpEL(path.split("\\/"));
}
private static String pathNodesToSpEL(String[] pathNodes) {
StringBuilder spelBuilder = new StringBuilder();
for(int i=0; i < pathNodes.length; i++) {
String pathNode = pathNodes[i];
if (pathNode.length() == 0) {
continue;
}
if ("~".equals(pathNode)) {
spelBuilder.append("[size() - 1]");
continue;
}
try {
int index = Integer.parseInt(pathNode);
spelBuilder.append('[').append(index).append(']');
} catch (NumberFormatException e) {
if (spelBuilder.length() > 0) {
spelBuilder.append('.');
}
spelBuilder.append(pathNode);
}
}
String spel = spelBuilder.toString();
if (spel.length() == 0) {
spel = "#this";
}
return spel;
}
@SuppressWarnings("unchecked")
private static <T> T[] copyOf(T[] original, int newLength) {
return (T[]) copyOf(original, newLength, original.getClass());
}
// reproduces Arrays.copyOf because that API is missing on Android 2.2
private static <T, U> T[] copyOf(U[] original, int newLength,
Class<? extends T[]> newType) {
@SuppressWarnings("unchecked")
T[] copy = ((Object) newType == (Object) Object[].class) ? (T[]) new Object[newLength]
: (T[]) Array.newInstance(newType.getComponentType(), newLength);
System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength));
return copy;
}
}