/*
* Beanfabrics Framework Copyright (C) by Michael Karneim, beanfabrics.org
* Use is subject to license terms. See license.txt.
*/
// TODO javadoc - remove this comment only when the class and all non-public
// methods and fields are documented
package org.beanfabrics;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.beanfabrics.model.PresentationModel;
import org.beanfabrics.support.PropertySupport;
/**
* @author Michael Karneim
*/
public class PathEvaluation implements Iterable<PathEvaluation.Entry> {
private final PresentationModel root;
private final Path path;
private final List<Entry> entries;
private final boolean resolvedCompletely;
private final int resolvedLength;
private final Path resolvedPath;
public PathEvaluation(PresentationModel root, Path path) {
if (path == null) {
throw new IllegalArgumentException("path==null");
}
if (root == null) {
throw new IllegalArgumentException("root==null");
}
List<Entry> entries = new ArrayList<Entry>(path.length() + 1);
// always add an entry for the root
entries.add(new Entry(null, Path.THIS_PATH_ELEMENT, root, 0));
int resolved = 0;
PresentationModel pModel = root;
Iterator<String> it = path.iterator();
while (it.hasNext() && pModel != null) {
PresentationModel owner = pModel;
String pathElement = it.next();
PropertySupport support = PropertySupport.get(owner);
pModel = support.getProperty(pathElement);
entries.add(new Entry(owner, pathElement, pModel, resolved + 1));
if (pModel == null) {
// no value found with that name
// -> check if it's declared
Class<? extends PresentationModel> declType = support.getPropertyType(pathElement);
if (declType != null) {
resolved++; // count it as 'resolved'
}
} else {
resolved++; // count it as 'resolved'
}
}
this.root = root;
this.path = path;
this.entries = entries;
this.resolvedLength = resolved;
this.resolvedCompletely = resolved == path.length();
this.resolvedPath = path.getSubPath(0, resolved);
}
public PresentationModel getRoot() {
return root;
}
public int getResolvedLength() {
return resolvedLength;
}
public boolean isCompletelyResolved() {
return resolvedCompletely;
}
public Path getPath() {
return this.path;
}
public Path getResolvedPath() {
return resolvedPath;
}
/** {@inheritDoc} */
public Iterator<Entry> iterator() {
return new Iterator<Entry>() {
private Iterator<Entry> delegate = entries.iterator();
public boolean hasNext() {
return delegate.hasNext();
}
public Entry next() {
return delegate.next();
}
public void remove() {
throw new UnsupportedOperationException("remove is not supported by this iterator");
}
};
}
public PathEvaluation.Entry getLastEntry() {
return entries.get(entries.size() - 1);
}
// TODO (mk) possibly add method getTargetProperty(): IProperty
// TODO (mk) possibly rename to getTargetEntry()
public PathEvaluation.Entry getResult() {
if (this.resolvedCompletely == false) {
throw new IllegalStateException("could not evaluate this path completely. Resolved '" + this.path.getSubPath(0, this.resolvedLength) + "'");
} else {
return getLastEntry();
}
}
public final class Entry {
private final PresentationModel owner;
private final String name;
private final PresentationModel value;
private final int index;
protected Entry(PresentationModel owner, String name, PresentationModel value, int index) {
this.owner = owner;
this.name = name;
this.value = value;
this.index = index;
}
public PresentationModel getOwner() {
return owner;
}
public String getName() {
return name;
}
public PresentationModel getValue() {
return value;
}
public boolean isLast() {
return index == path.length();
}
}
/// static
/**
* Evaluates the given path at the given root and returns the value at the
* end.
*
* @param root
* @param path
*/
public static PresentationModel evaluate(PresentationModel root, Path path)
throws IllegalArgumentException, EvaluationException {
if (path == null) {
throw new IllegalArgumentException("path==null");
}
PathEvaluation eval = new PathEvaluation(root, path);
if (eval.isCompletelyResolved()) {
return eval.getResult().value;
} else {
throw new EvaluationException("Can't evaluate path completely. Evaluated: " + eval.getPath().getSubPath(0, eval.getResolvedLength()));
}
}
/**
* Evaluates the given path at the given root and returns the value at the
* end if it can be found, null otherwise.
*
* @param root
* @param path
*/
public static PresentationModel evaluateOrNull(PresentationModel root, Path path)
throws IllegalArgumentException {
if (path == null) {
return null;
}
PathEvaluation eval = new PathEvaluation(root, path);
if (eval.isCompletelyResolved()) {
return eval.getResult().value;
} else {
return null;
}
}
}