/*******************************************************************************
* Copyright (c) 2014 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:
* Christoph Caks <ccaks@bestsolution.at> - initial API and implementation
* Martin Platter <mplatter@bestsolution.at> - filter implementation
* Martin Bluehweis <martin.bluehweis@bestsolution.at> - improvements & bugfixes
*******************************************************************************/
package at.bestsolution.emf.navi;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
/**
*
* @author Christoph Caks, Martin Platter
*
*/
public class FeaturePathUtil {
public static interface PathSegmentResolver {
List<Object> resolveSegment(EObject owner, FeaturePathSegment segment, boolean lastSegment) throws PathNotTraversableException, TooManyResultsException;
}
public static PathSegmentResolver GET_FIRST_RESOLVER = new PathSegmentResolver() {
@Override
public List<Object> resolveSegment(EObject owner, FeaturePathSegment segment, boolean lastSegment) throws PathNotTraversableException {
if (!owner.eClass().getEAllStructuralFeatures().contains(segment.feature)) {
throw new PathNotTraversableException("Object " + owner + " does not have " + segment.feature);
}
List<Object> result = new ArrayList<Object>();
if (segment.feature.isMany()) {
// TODO NULL HANDLING
final List<Object> candidates = (List<Object>) owner.eGet(segment.feature);
for (Object candidate : candidates) {
if (segment.condition.matches(candidate)) {
result.add(candidate);
break; // we always take the first match
}
}
}
else {
// TODO NULL HANDLING
final Object resolved = owner.eGet(segment.feature);
result.add(resolved);
}
return result;
}
};
public static PathSegmentResolver GET_RESOLVER = new PathSegmentResolver() {
@Override
public List<Object> resolveSegment(EObject owner, FeaturePathSegment segment, boolean lastSegment) throws PathNotTraversableException, TooManyResultsException {
if (!owner.eClass().getEAllStructuralFeatures().contains(segment.feature)) {
throw new PathNotTraversableException("Object " + owner + " does not have " + segment.feature);
}
List<Object> result = new ArrayList<Object>();
if (segment.feature.isMany()) {
// TODO NULL HANDLING
final List<Object> candidates = (List<Object>) owner.eGet(segment.feature);
for (Object candidate : candidates) {
if (segment.condition.matches(candidate)) {
if (!lastSegment && !result.isEmpty()) {
throw new TooManyResultsException();
}
result.add(candidate);
}
}
}
else {
// TODO NULL HANDLING
final Object resolved = owner.eGet(segment.feature);
result.add(resolved);
}
return result;
}
};
public static PathSegmentResolver FILTER_RESOLVER = new PathSegmentResolver() {
@Override
public List<Object> resolveSegment(EObject owner, FeaturePathSegment segment, boolean lastSegment) throws PathNotTraversableException {
if (!owner.eClass().getEAllStructuralFeatures().contains(segment.feature)) {
throw new PathNotTraversableException("Object " + owner + " does not have " + segment.feature);
}
List<Object> result = new ArrayList<Object>();
if (segment.feature.isMany()) {
// TODO NULL HANDLING
final List<Object> candidates = (List<Object>) owner.eGet(segment.feature);
for (Object candidate : candidates) {
if (segment.condition.matches(candidate)) {
result.add(candidate);
}
}
}
else {
// TODO NULL HANDLING
final Object resolved = owner.eGet(segment.feature);
result.add(resolved);
}
return result;
}
};
protected static void resolvePath(EObject currentOwner, int currentSegmentIdx, FeaturePath path, PathSegmentResolver resolver, List<Object> result, FeaturePathCallback callback, Object memento) throws PathNotTraversableException, TooManyResultsException {
final FeaturePathSegment currentSegment = path.getSegments().get(currentSegmentIdx);
final boolean lastSegment = currentSegmentIdx == path.getSegments().size() - 1;
final List<Object> nextResult = resolver.resolveSegment(currentOwner, currentSegment, lastSegment);
if (lastSegment) {
result.addAll(nextResult);
if (callback != null) {
for (Object leaf : nextResult) {
callback.runOnLeaf(memento, leaf);
}
}
}
else {
if (callback != null) {
memento = callback.runOnNode(memento, currentOwner, currentSegment);
}
for (final Object nextOwner : nextResult) {
if (nextOwner instanceof EObject) {
resolvePath((EObject)nextOwner, currentSegmentIdx + 1, path, resolver, result, callback, memento);
}
}
}
}
public static List<Object> resolvePath(EObject owner, FeaturePath path, PathSegmentResolver resolver, FeaturePathCallback callback) throws PathNotTraversableException, TooManyResultsException {
List<Object> result = new ArrayList<Object>();
resolvePath(owner, 0, path, resolver, result, callback, null);
return result;
}
public static List<Object> doFilter(EObject owner, FeaturePath path) throws PathNotTraversableException {
try {
return resolvePath(owner, path, FILTER_RESOLVER, null);
} catch (TooManyResultsException e) {
// filter wont throw this one
return null;
}
}
public static Object doGetFirst(EObject owner, FeaturePath path) throws PathNotTraversableException {
try {
return resolvePath(owner, path, GET_FIRST_RESOLVER, null).get(0);
} catch (TooManyResultsException e) {
// get first wont throw this one
return null;
}
}
public static Object doGet(EObject owner, FeaturePath path) throws PathNotTraversableException, TooManyResultsException {
final List<Object> result = resolvePath(owner, path, GET_RESOLVER, null);
if (path.getSegments().get(path.getSegments().size()-1).feature.isMany()) {
return result;
}
else {
return result.get(0);
}
}
public static Object get(EObject owner, FeaturePath path) throws PathNotTraversableException, TooManyResultsException {
return doGet(owner, path);
// TODO needs to be reimplemented
// Object current = owner;
// EObject eCurrent = owner;
// Iterator<FeaturePathSegment> it = path.getSegments().iterator();
// while (it.hasNext()) {
// final FeaturePathSegment segment = it.next();
// if (!eCurrent.eClass().getEAllStructuralFeatures()
// .contains(segment.feature)) {
// throw new IllegalArgumentException(
// "Invalid feature path segment: "
// + eCurrent.eClass().getName()
// + " does not contain feature "
// + segment.feature.getName());
// }
// Object newCurrent = null;
// if (segment.feature.isMany()) {
// // handle collection
// Object many = eCurrent.eGet(segment.feature);
// if (many instanceof List) {
// @SuppressWarnings("unchecked")
// List<Object> manyList = (List<Object>) many;
// List<Object> result = new ArrayList<Object>();
// for (Object o : manyList) {
// if (o instanceof EObject) {
// EObject eo = (EObject) o;
// if (segment.condition.matches(eo)) {
// result.add(eo);
// }
// }
// }
// if (!it.hasNext()) {
// newCurrent = result;
// } else {
// switch (result.size()) {
// case 0:
// // TODO this needs configurable handling
// return null;
// case 1:
// newCurrent = result.get(0);
// break;
// default:
// throw new TooManyResultsException(
// "expected 0 or 1 result, but got many: " + result); //$NON-NLS-1$
// }
// }
// }
// } else {
//
// newCurrent = eCurrent.eGet(segment.feature);
// }
//
// current = newCurrent;
//
// if (it.hasNext()) {
// if (current instanceof EObject) {
// eCurrent = (EObject) current;
// }
// // if (current == null) {
// // throw new NullPointerException("last segment was null! " +
// // eCurrent);
// // }
// else {
// throw new NullPointerException("could not resolve segment "
// + segment + " after " + eCurrent);
// }
// }
// }
// return current;
}
/**
* get the first object in a given feature path
* @param owner
* owner object
* @param path
* feature path
* @return first found object
*/
public static Object first(EObject owner, FeaturePath path) throws PathNotTraversableException {
return doGetFirst(owner, path);
// // TODO needs to be reimplemented
// Object current = owner;
// EObject eCurrent = owner;
// Iterator<FeaturePathSegment> it = path.getSegments().iterator();
// while (it.hasNext()) {
// final FeaturePathSegment segment = it.next();
// if (!eCurrent.eClass().getEAllStructuralFeatures()
// .contains(segment.feature)) {
// throw new IllegalArgumentException(
// "Invalid feature path segment: "
// + eCurrent.eClass().getName()
// + " does not contain feature "
// + segment.feature.getName());
// }
// Object newCurrent = null;
// if (segment.feature.isMany()) {
// // handle collection
// Object many = eCurrent.eGet(segment.feature);
// if (many instanceof List) {
// @SuppressWarnings("unchecked")
// List<Object> manyList = (List<Object>) many;
//
// for (Object o : manyList) {
// if (o instanceof EObject) {
// EObject eo = (EObject) o;
// if (segment.condition.matches(eo)) {
// newCurrent = eo;
// break;
// }
// }
// }
// }
// } else {
// newCurrent = eCurrent.eGet(segment.feature);
// }
//
// current = newCurrent;
//
// if (it.hasNext()) {
// if (current instanceof EObject) {
// eCurrent = (EObject) current;
// }
// else {
// throw new NullPointerException("could not resolve segment "
// + segment + " after " + eCurrent);
// }
// }
// }
// return current;
}
public static List<Object> filter(EObject owner, FeaturePath path) {
FeaturePathSegment[] segs = path.getSegments().toArray(
new FeaturePathSegment[0]);
return filter(owner, segs, null);
}
public static List<Object> filter(EObject owner, FeaturePathSegment[] segs,
FeaturePathCallback cb) {
List<Object> result = new ArrayList<Object>();
filter(owner, segs, 0, result, cb, null);
return result;
}
@SuppressWarnings("rawtypes")
public static void filter(EObject owner, FeaturePathSegment[] path,
int idx, List<Object> result, FeaturePathCallback cb, Object memento) {
EStructuralFeature feature = path[idx].feature;
if (!owner.eClass().getEAllStructuralFeatures().contains(feature)) {
throw new IllegalArgumentException("Invalid feature path segment: "
+ owner.eClass().getName() + " does not contain feature "
+ feature.getName());
}
Object obj = owner.eGet(feature);
if (cb != null) {
memento = cb.runOnNode(memento, owner, path[idx]);
}
if (feature.isMany()) {
for (Object o : (List) obj) {
if (path[idx].condition.matches((EObject) o)) {
filterInternalRecursion((EObject) o, path, idx, result, cb,
memento);
}
}
} else {
filterInternalRecursion((EObject) obj, path, idx, result, cb,
memento);
}
}
private static void filterInternalRecursion(EObject owner,
FeaturePathSegment[] path, int idx, List<Object> result,
FeaturePathCallback cb, Object memento) {
if (idx == path.length - 1) {
if (cb != null) {
cb.runOnLeaf(memento, owner, path[idx]);
}
result.add(owner);
} else {
filter(owner, path, idx + 1, result, cb, memento);
}
}
}