Wednesday, September 24, 2008

Simple Query-Language for Dynamic EMF Model

Usually I'm generating the model code from my ecore models. But sometimes I want to simply query a model, and in these cases a dynamic EMF model is good enough. I wrote a small helper class, providing a very simple query language for dynamic EMF very similar to the JSP Expression Language -- of course less powerful. You can query an EObject using a dot notation and square brakets for defining the index of a list item. E.g.
Object street = eval( eobj, "person.address[0].street");
will return the street of the first address entry. The utility class will create nice error messages if something goes wrong. This is the grammar of the language:

query    := feature ( "." feature )*
feature  := <name> ( "[" <index> "]" )?
Maybe there's another way of accessing dynamic EMF that easy, maybe using some (at least for me: hidden) EMF utility class. If you know a better solution, please let me know! Here is the utility class:
/**
* Author: jevopi
* (C) 2008 jevopi
*/
public class DynEcoreUtil {

 /**
  * Returns value of specified feature path. For specifying the feature path,
  * a dot notation is used, if a list is to be parsed, square brakets with
  * index are used.
  *
  * @param obj
  * @param featurePath
  * @return
  */
 public static Object eval(Object obj, String featurePath) {
  StringTokenizer st = new StringTokenizer(featurePath, ".");
  String feature = "";
  Object value = obj;
  try {
   while (st.hasMoreTokens()) {
    obj = value;
    feature = st.nextToken();
    if (feature.endsWith("]")) {
     int splitPos = feature.lastIndexOf("[");
     int index = Integer.parseInt(feature.substring(
       splitPos + 1, feature.length() - 1));
     feature = feature.substring(0, splitPos);
     value = fvl((EObject) obj, feature, index);
    } else {
     value = fv((EObject) obj, feature);
    }
   }
   return value;

  } catch (Exception ex) {
   if (feature == null || !(obj instanceof EObject)) {
    throw new IllegalArgumentException("Can't resolve "
      + featurePath + ", feature " + feature + " not found.",
      ex);
   } else {
    EObject eobj = (EObject) obj;
    EClass eclass = eobj.eClass();
    StringBuffer strb = new StringBuffer("Can't resolve "
      + featurePath + ", feature " + feature + " not found.");
    strb.append("Possible features of type " + eclass.getName());
    strb.append(": ");
    boolean bFirst = true;
    for (EStructuralFeature sf: eclass.getEStructuralFeatures()) {
     if (!bFirst) strb.append(", "); else bFirst = false;
     strb.append(sf.getName());
    }
    throw new IllegalArgumentException(strb.toString(), ex);
   }
  }
 }

 static Object fv(EObject obj, String name) {
  EStructuralFeature feature = obj.eClass().getEStructuralFeature(name);
  return obj.eGet(feature);
 }

 static Object fvl(EObject obj, String name, int index) {
  EList<object> list = (EList<object>) fv(obj, name);
  return list.get(index);
 }
}