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