public Property getProperty(Path path) {
   Path parent = path.getParent();
   if (isSource(parent)) {
     return getSource(parent).getObjectType().getProperty(path.getName());
   } else {
     Property prop = getProperty(parent);
     if (prop == null) {
       throw new IllegalArgumentException("no such property in signature: " + path);
     } else {
       return prop.getType().getProperty(path.getName());
     }
   }
 }
  private void addDefaultProperties(Path path) {
    ObjectType type = getType(path);
    addFetchedPaths(path, type);

    if (!isSource(path)) {
      Root root = type.getRoot();
      Property prop = getProperty(path);
      // assume that path.getParent() is keyed
      ObjectMap container = root.getObjectMap(prop.getContainer());
      if (container != null) {
        makePathLoadable(path.getParent(), container.getDeclaredFetchedPaths());
      }
    }
  }
  /** Add all leaves of key property hierarchy */
  private void addPathImmediates(Path path) {
    ObjectType type = getType(path);
    Collection props = type.getImmediateProperties();
    // all props for unkeyed, immediate only for keyed

    if (props.size() == 0) {
      if (!m_paths.contains(path)) {
        m_paths.add(path);
      }
    } else {
      for (Iterator it = props.iterator(); it.hasNext(); ) {
        Property prop = (Property) it.next();
        addPathImmediates(Path.add(path, prop.getName()));
      }
    }
  }
  public Collection getReachablePropertyEvents(Object obj) {
    ArrayList result = new ArrayList();

    ObjectType ot = m_ssn.getObjectType(obj);
    for (Iterator it = ot.getProperties().iterator(); it.hasNext(); ) {
      Property prop = (Property) it.next();
      if (prop.isCollection()) {
        result.addAll(getCurrentEvents(obj, prop));
      } else {
        Event e = getLastEvent(obj, prop);
        if (e != null) {
          result.add(e);
        }
      }
    }

    return result;
  }
 public PropertyEvent getLastEvent(Object obj, Property prop) {
   if (prop.isCollection()) {
     LinkedList lst = (LinkedList) m_collectionEvents.get(getKey(obj, prop));
     if (lst == null) {
       return null;
     }
     return (PropertyEvent) lst.getLast();
   }
   return (PropertyEvent) m_setEvents.get(getKey(obj, prop));
 }
  public Collection getCurrentEvents(Object obj, Property prop) {
    if (!prop.isCollection()) {
      throw new IllegalArgumentException();
    }

    List evs = (List) m_collectionEvents.get(getKey(obj, prop));
    if (evs == null) {
      return Collections.EMPTY_LIST;
    }
    return evs;
  }
  public PropertyEvent getLastEvent(Object obj, Property prop, Object arg) {
    if (!prop.isCollection()) {
      throw new IllegalArgumentException();
    }

    List events = (List) m_collectionEvents.get(getKey(obj, prop));

    if (events == null) {
      return null;
    }

    for (int i = events.size() - 1; i >= 0; i--) {
      PropertyEvent old = (PropertyEvent) events.get(i);
      if (old.getArgument().equals(arg)) {
        return old;
      }
    }

    return null;
  }