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