/*
* 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.
*/
/*******************************************************************************
* Copyright (c) 2010 BestSolution.at and others. All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License v1.0 which accompanies this distribution, and is
* available at http://www.eclipse.org/legal/epl-v10.html
*
* Contributors: Tom Schindl <tom.schindl@bestsolution.at> - adjustment to EObject
******************************************************************************/
package org.eclipse.e4.emf.internal.xpath.helper;
import java.lang.reflect.Array;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.jxpath.Container;
import org.apache.commons.jxpath.JXPathException;
import org.apache.commons.jxpath.util.TypeUtils;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
/**
* Collection and property access utilities.
*/
public class ValueUtils {
public static Object getValue(EObject bean, EStructuralFeature pd) {
return bean.eGet(pd);
}
/**
* Returns true if the object is an array or a Collection.
*
* @param value
* to test
* @return boolean
*/
public static boolean isCollection(Object value) {
value = getValue(value);
if (value == null) {
return false;
}
if (value.getClass().isArray()) {
return true;
}
if (value instanceof Collection) {
return true;
}
return false;
}
/**
* If the parameter is a container, opens the container and return the contents. The method is recursive.
*
* @param object
* to read
* @return Object
*/
public static Object getValue(Object object) {
while (object instanceof Container) {
object = ((Container) object).getValue();
}
return object;
}
/**
* Returns the length of the supplied collection. If the supplied object is not a collection, returns 1. If
* collection is null, returns 0.
*
* @param collection
* to check
* @return int
*/
public static int getLength(Object collection) {
if (collection == null) {
return 0;
}
collection = getValue(collection);
if (collection.getClass().isArray()) {
return Array.getLength(collection);
}
if (collection instanceof Collection) {
return ((Collection<?>) collection).size();
}
return 1;
}
/**
* Remove the index'th element from the supplied collection.
*
* @param collection
* to edit
* @param index
* int
* @return the resulting collection
*/
public static Object remove(Object collection, int index) {
collection = getValue(collection);
if (collection == null) {
return null;
}
if (index >= getLength(collection)) {
throw new JXPathException("No such element at index " + index);
}
if (collection.getClass().isArray()) {
int length = Array.getLength(collection);
Object smaller = Array.newInstance(collection.getClass().getComponentType(), length - 1);
if (index > 0) {
System.arraycopy(collection, 0, smaller, 0, index);
}
if (index < length - 1) {
System.arraycopy(collection, index + 1, smaller, index, length - index - 1);
}
return smaller;
}
if (collection instanceof List) {
int size = ((List<?>) collection).size();
if (index < size) {
((List<?>) collection).remove(index);
}
return collection;
}
if (collection instanceof Collection) {
Iterator<?> it = ((Collection<?>) collection).iterator();
for (int i = 0; i < index; i++) {
if (!it.hasNext()) {
break;
}
it.next();
}
if (it.hasNext()) {
it.next();
it.remove();
}
return collection;
}
throw new JXPathException("Cannot remove " + collection.getClass().getName() + "[" + index + "]");
}
@SuppressWarnings("unchecked")
public static Object getValue(EObject bean, EStructuralFeature pd, int index) {
if (pd.isMany()) {
try {
return ((List<Object>) bean.eGet(pd)).get(index);
}
catch (IndexOutOfBoundsException ex) {
return null;
}
catch (Throwable ex) {
throw new JXPathException("Cannot access property: " + pd.getName(), ex);
}
}
// We will fall through if there is no indexed read
return getValue(getValue(bean, pd), index);
}
@SuppressWarnings("unchecked")
public static Object getValue(Object collection, int index) {
collection = getValue(collection);
Object value = collection;
if (collection != null) {
if (collection.getClass().isArray()) {
if (index < 0 || index >= Array.getLength(collection)) {
return null;
}
value = Array.get(collection, index);
}
else if (collection instanceof List) {
if (index < 0 || index >= ((List<?>) collection).size()) {
return null;
}
value = ((List<Object>) collection).get(index);
}
else if (collection instanceof Collection) {
int i = 0;
Iterator<Object> it = ((Collection<Object>) collection).iterator();
for (; i < index; i++) {
it.next();
}
if (it.hasNext()) {
value = it.next();
}
else {
value = null;
}
}
}
return value;
}
public static int getCollectionHint(Class<?> clazz) {
if (clazz.isArray()) {
return 1;
}
if (Collection.class.isAssignableFrom(clazz)) {
return 1;
}
if (clazz.isPrimitive()) {
return -1;
}
if (clazz.isInterface()) {
return 0;
}
if (Modifier.isFinal(clazz.getModifiers())) {
return -1;
}
return 0;
}
@SuppressWarnings("unchecked")
public static void setValue(EObject bean, EStructuralFeature pd, Object value) {
try {
if (pd.isMany()) {
List<Object> l = (List<Object>) bean.eGet(pd);
l.clear();
l.addAll((Collection<Object>) value);
}
else {
bean.eSet(pd, value);
}
}
catch (Exception ex) {
throw new JXPathException("Cannot modify property: " + (bean == null ? "null" : bean.getClass().getName())
+ "." + pd.getName(), ex);
}
}
public static void setValue(EObject bean, EStructuralFeature pd, int index, Object value) {
if (pd.isMany()) {
try {
@SuppressWarnings("unchecked")
List<Object> l = (List<Object>) bean.eGet(pd);
l.set(index, convert(value, pd.getEType().getInstanceClass()));
}
catch (Exception ex) {
throw new RuntimeException("Cannot access property: " + pd.getName() + ", " + ex.getMessage());
}
}
// We will fall through if there is no indexed read
Object collection = getValue(bean, pd);
if (isCollection(collection)) {
setValue(collection, index, value);
}
else if (index == 0) {
setValue(bean, pd, value);
}
else {
throw new RuntimeException("Not a collection: " + pd.getName());
}
}
@SuppressWarnings("unchecked")
public static void setValue(Object collection, int index, Object value) {
collection = getValue(collection);
if (collection != null) {
if (collection.getClass().isArray()) {
Array.set(collection, index, convert(value, collection.getClass().getComponentType()));
}
else if (collection instanceof List) {
((List<Object>) collection).set(index, value);
}
else if (collection instanceof Collection) {
throw new UnsupportedOperationException("Cannot set value of an element of a "
+ collection.getClass().getName());
}
}
}
private static Object convert(Object value, Class<?> type) {
try {
return TypeUtils.convert(value, type);
}
catch (Exception ex) {
throw new JXPathException("Cannot convert value of class "
+ (value == null ? "null" : value.getClass().getName()) + " to type " + type, ex);
}
}
public static Object expandCollection(Object collection, int size) {
if (collection == null) {
return null;
}
if (size < getLength(collection)) {
throw new JXPathException("adjustment of " + collection + " to size " + size + " is not an expansion");
}
if (collection.getClass().isArray()) {
Object bigger = Array.newInstance(collection.getClass().getComponentType(), size);
System.arraycopy(collection, 0, bigger, 0, Array.getLength(collection));
return bigger;
}
if (collection instanceof Collection) {
@SuppressWarnings("unchecked")
Collection<Object> c = (Collection<Object>) collection;
while (c.size() < size) {
c.add(null);
}
return collection;
}
throw new JXPathException("Cannot turn " + collection.getClass().getName() + " into a collection of size "
+ size);
}
}