private IModelProperty resolvePropertyPath(final String propPath) throws PropertyPathException {
if(StringUtil.isEmpty(propPath)) {
throw new MalformedPropPathException("No property path specified.");
}
final PropertyPath pp = new PropertyPath(propPath);
IModelProperty prop = null;
Model model = this;
final int len = pp.depth();
for(int i = 0; i < len; i++) {
final String pname = pp.nameAt(i);
final int index;
try {
index = pp.indexAt(i);
}
catch(final IllegalArgumentException e) {
throw new MalformedPropPathException(e.getMessage());
}
final boolean indexed = (index >= 0);
final boolean atEnd = (i == len - 1);
// find the prop val under current model
prop = model.get(pname);
if(prop == null) {
if(atEnd) {
throw new UnsetPropertyException(pp.toString(), model, pname);
}
throw new NullNodeInPropPathException(pp.toString(), pname);
}
// get the bound prop val type for this prop path element
final PropertyType pvType = prop.getType();
// non-relational prop val
if(!pvType.isRelational()) {
if(!atEnd) {
throw new PropPathNodeMismatchException(pp.toString(), pname, pvType.toString(), "Relational");
}
return prop;
}
// related one prop val
else if(pvType == PropertyType.RELATED_ONE) {
if(indexed) {
throw new PropPathNodeMismatchException(pp.toString(), pname, pvType.toString(), PropertyType.RELATED_MANY
.toString());
}
final IModelRefProperty mrp = (IModelRefProperty) prop;
if(atEnd) {
return mrp;
}
// get the nested group...
final Model ng = mrp.getModel();
if(ng == null) {
throw new NullNodeInPropPathException(pp.toString(), pname);
}
// reset for next path
model = ng;
}
// related many prop val
else if(pvType == PropertyType.RELATED_MANY) {
final RelatedManyProperty rmp = (RelatedManyProperty) prop;
if(!indexed) {
if(atEnd) {
return rmp;
}
// an index is expected if we're not at the end
throw new MalformedPropPathException(pp.toString());
}
else if(indexed) {
// get the nested group prop val list...
if(index >= rmp.size()) {
throw new IndexOutOfRangeInPropPathException(pp.toString(), pname, index);
}
if(atEnd) {
return rmp.getIndexedProperty(index);
}
// reset for next path