Interface TraceObjectSchema

All Known Implementing Classes:
DefaultTraceObjectSchema, PrimitiveTraceObjectSchema

public interface TraceObjectSchema
Type information for a particular value or TraceObject

This allows a client to inspect predictable aspects of a model before fetching any actual objects. This also helps a client understand where to listen for particular types of objects and comprehend the model's structure in general.

For a primitive type, the type is given by getType(). For TraceObjects, supported interfaces are given by getInterfaces(). The types of children are determined by matching on the keys (indices and names), the result being a subordinate TraceObjectSchema. Keys must match exactly, unless the "pattern" is the empty string, which matches any key. Similarly, the wild-card index is [].

The schema can specify attribute aliases, which implies that a particular key ("from") will always have the same value as another ("to"). As a result, the schemas of aliased keys will also implicitly match.

  • Method Details

    • getContext

      SchemaContext getContext()
      Get the context of which this schema is a member

      All schema names are resolved in this same context

      Returns:
      the context
    • getName

      Get the name of this schema
      Returns:
      the name
    • getType

      Class<?> getType()
      Get the Java class that best represents this type.

      Note that this is either a primitive, or TraceObject. Even though an object implementation is necessarily a sub-type of TraceObject, for any object schema, this return TraceObject. Information about a "sub-type" of object is communicated via interfaces, element schemas, and attribute schemas.

      Returns:
      the Java class for this type
    • getInterfaces

      Set<Class<? extends TraceObjectInterface>> getInterfaces()
      Get the minimum interfaces supported by a conforming object
      Returns:
      the set of required interfaces
    • isCanonicalContainer

      boolean isCanonicalContainer()
      Check if this object is the canonical container for its elements

      This is generally in reference to the default type of this object's elements. For example, if elements of this object are all expected to support the "Process" interface, then this is the canonical Process container. Any Process ought to have a (canonical) path in this container. Any other path referring to such a Process ought to be a link.

      NOTE: the concept of links is still in incubation, as some native debugging APIs seem to have made it difficult to detect object identity. Additionally, it's possible a caller's first encounter with an object is not via its canonical path, and it may be difficult to assign a path having only the native-API-given object in hand.

      Returns:
      true if this is a canonical container, false otherwise
    • getElementSchemas

      Get the map of element indices to named schemas

      It is uncommon for this map to be populated, since the elements of a given container are typically uniform in type. Nevertheless, there can be restrictions imposed on -- and information provided for -- specific indices.

      Returns:
      the map
    • getDefaultElementSchema

      default TraceObjectSchema.SchemaName getDefaultElementSchema()
      Get the default schema for elements

      Since elements of a given container are typically uniform in type, this is the primary means of specifying element schemas.

      Returns:
      the default named schema
    • getElementSchema

      default TraceObjectSchema.SchemaName getElementSchema(String index)
      Get the named schema for a given element index

      If there's a schema specified for the given index, that schema is taken. Otherwise, the default element schema is taken.

      Parameters:
      index - the index
      Returns:
      the named schema
    • getAttributeSchemas

      Get the map of attribute names to named schemas

      The returned map will include aliases. To determine whether or not an attribute key is an alias, check whether the entry's key matches the name of the attribute (see TraceObjectSchema.AttributeSchema.getName()). It is possible the schema's name is empty, i.e., the default schema. This indicates an alias to a key that was not named in the schema. Use getAttributeAliases() to determine the name of that key.

      Returns:
      the map
    • getAttributeAliases

      Map<String,String> getAttributeAliases()
      Get the map of attribute name aliases

      The returned map must provide the direct alias names. For any given key, the client need only query the map once to determine the name of the attribute to which the alias refers. Consequently, the map also cannot indicate a cycle.

      An aliased attribute takes the value of its target implicitly.

      Returns:
      the map
    • checkAliasedAttribute

      default String checkAliasedAttribute(String name)
      Check if the given name is an alias and get the target attribute name
      Parameters:
      name - the name
      Returns:
      the alias' target, or the given name if not an alias
    • getDefaultAttributeSchema

      default TraceObjectSchema.AttributeSchema getDefaultAttributeSchema()
      Get the default schema for attributes

      Since the expected attributes and their respective schemas are generally enumerated, this most commonly returns TraceObjectSchema.AttributeSchema.DEFAULT_ANY, to allow unrestricted use of additional attributes, or TraceObjectSchema.AttributeSchema.DEFAULT_VOID, to forbid any additional attributes.

      Returns:
      the default attribute schema
    • getAttributeSchema

      default TraceObjectSchema.AttributeSchema getAttributeSchema(String name)
      Get the attribute schema for a given attribute name

      If there's a schema specified for the given name, that schema is taken. If the name refers to an alias, its schema is taken. Otherwise, the default attribute schema is taken.

      Parameters:
      name - the name
      Returns:
      the attribute schema
    • getChildSchemaName

      default TraceObjectSchema.SchemaName getChildSchemaName(String key)
      Get the named schema for a child having the given key
      Parameters:
      key - the key
      Returns:
      the named schema
    • getChildSchema

      default TraceObjectSchema getChildSchema(String key)
      Get the schema for a child having the given key

      This is the preferred method for navigating a schema and computing the expected type of a child.

      Parameters:
      key - the key
      Returns:
      the schema
    • getSuccessorSchema

      default TraceObjectSchema getSuccessorSchema(KeyPath path)
      Get the schema for a successor at the given (sub) path

      If this is the schema of the root object, then this gives the schema of the object at the given path in the model. This will always give a non-null result, though that result might be PrimitiveTraceObjectSchema.VOID.

      Parameters:
      path - the relative path from an object having this schema to the desired successor
      Returns:
      the schema for the successor
    • getSuccessorSchemas

      default List<TraceObjectSchema> getSuccessorSchemas(KeyPath path)
      Get the list of schemas traversed from this schema along the given (sub) path

      This list always begins with this schema, followed by the child schema for each key in the path. Thus, for a path of length n, the resulting list has n+1 entries. This is useful for searches along the ancestry of a given path:

      
       List<TargetObjectSchema> schemas = getSuccessorSchemas(path);
       for (; path != null; path = PathUtils.parent(path)) {
       	TargetObjectSchema schema = schemas.get(path.size());
       	// ...
       }
       

      All entries are non-null, though they may be PrimitiveTraceObjectSchema.VOID.

      Parameters:
      path - the relative path from an object having this schema to the desired successor
      Returns:
      the list of schemas traversed, ending with the successor's schema
    • searchFor

      default PathFilter searchFor(Class<? extends TraceObjectInterface> type, boolean requireCanonical)
      Do the same as searchFor(Class, KeyPath, boolean) with an empty prefix
      Parameters:
      type - the sub-type of TraceObjectInterface to search for
      requireCanonical - only return patterns matching a canonical location for the type
      Returns:
      a set of patterns where such objects could be found
    • searchFor

      default PathFilter searchFor(Class<? extends TraceObjectInterface> type, KeyPath prefix, boolean requireCanonical)
      Find (sub) path patterns that match objects implementing a given interface

      Each returned path pattern accepts relative paths from an object having this schema to a successor implementing the interface.

      Parameters:
      type - the sub-type of TraceObjectInterface to search for
      prefix - the prefix for each relative path pattern
      requireCanonical - only return patterns matching a canonical location for the type
      Returns:
      a set of patterns where such objects could be found
    • searchForCanonicalContainer

      default KeyPath searchForCanonicalContainer(Class<? extends TraceObjectInterface> type)
      Find the (sub) path to the canonical container for objects implementing a given interface

      If more than one container is found having the shortest path, then null is returned.

      Parameters:
      type - the sub-type of TraceObjectInterface to search for
      Returns:
      the single path to that container
    • searchForSuitable

      default KeyPath searchForSuitable(Class<? extends TraceObjectInterface> type, KeyPath path)
      Search for a suitable object with this schema at the given path
      Parameters:
      type - the type of object sought
      path - the path of a seed object
      Returns:
      the expected path of the suitable object, or null
    • searchForSuitable

      default KeyPath searchForSuitable(TraceObjectSchema schema, KeyPath path)
      Search for a suitable object with this schema at the given path
      Parameters:
      schema - the schema of object sought
      path - the path of a seed object
      Returns:
      the expected path of the suitable object, or null
    • filterForSuitable

      default PathFilter filterForSuitable(Class<? extends TraceObjectInterface> type, KeyPath path)
      Search for all suitable objects with this schema at the given path

      This behaves like searchForSuitable(Class, KeyPath), except that it returns a matcher for all possibilities. Conventionally, when the client uses the matcher to find suitable objects and must choose from among the results, those having the longer paths should be preferred. More specifically, it should prefer those sharing the longer path prefixes with the given path. The client should not just take the first objects, since these will likely have the shortest paths. If exactly one object is required, consider using searchForSuitable(Class, KeyPath) instead.

      Parameters:
      type -
      path -
      Returns:
      the filter for finding objects
    • searchForSuitableContainer

      default KeyPath searchForSuitableContainer(Class<? extends TraceObjectInterface> type, KeyPath path)
      Like searchForSuitable(Class, KeyPath), but searches for the canonical container whose elements have the given type
      Parameters:
      type - the type of object sought
      path - the path of a seed object
      Returns:
      the expected path of the suitable container of those objects, or null
    • searchForAncestor

      default KeyPath searchForAncestor(Class<? extends TraceObjectInterface> type, KeyPath path)
      Find the nearest ancestor implementing the given interface along the given path

      If the given path implements the interface, it is returned, i.e., it is not strictly an ancestor.

      Parameters:
      type - the interface to search for
      path - the seed path
      Returns:
      the found path, or null if no ancestor implements the interface
    • searchForAncestorContainer

      default KeyPath searchForAncestorContainer(Class<? extends TraceObjectInterface> type, KeyPath path)
      Find the nearest ancestor which is the canonical container of the given interface

      If the given path is such a container, it is returned, i.e., it is not strictly an ancestor.

      Parameters:
      type - the interface whose canonical container to search for
      path - the seed path
      Returns:
      the found path, or null if no such ancestor was found
    • isHidden

      default boolean isHidden(String key)
      Check if the given key should be hidden for an object having this schema

      Elements ought never to be hidden. Otherwise, this defers to the attribute schema.

      Parameters:
      key - the child key to check
      Returns:
      true if hidden
    • validateTypeAndInterfaces

      default void validateTypeAndInterfaces(Object value, KeyPath parentPath, String key, boolean strict)
      Verify that the given value is of this schema's required type and, if applicable, implements the required interfaces
      Parameters:
      value - the value being assigned to the key
      parentPath - the path of the object whose key is being assigned, for diagnostics
      key - the key that is being assigned
      strict - true to throw an exception upon violation; false to just log and continue
    • validateRequiredAttributes

      default void validateRequiredAttributes(TraceObject object, boolean strict, long snap)
      Verify that all required attributes are present

      NOTE: This may become part of a schema and/or connector tester/validator later.

      Parameters:
      object - the object whose schema is this one
      strict - to throw exceptions upon violations
      snap - the relevant snapshot
    • searchForRegisterContainer

      default PathFilter searchForRegisterContainer(int frameLevel, KeyPath path)
      Search for a suitable register container

      This will try with and without considerations for frames. If the schema indicates that register containers are not contained within frames, then frameLevel must be 0, otherwise this will return empty. If dependent on frameLevel, this will return two singleton paths: one for a decimal index and another for a hexadecimal index. If not, this will return a singleton path. If it fails to find a unique container, this will return empty.

      NOTE: This must be used at the top of the search scope, probably the root schema. For example, to search the entire model for a register container related to myObject:

       for (PathPattern regPattern : myObject.getModel()
                      .getSchema()
                      .searchForRegisterContainer(0, myObject.getPath())) {
              TargetObject objRegs = myObject.getModel().getModelObject(regPattern.getSingletonPath());
              if (objRegs != null) {
                      // found it
              }
       }
       

      This places some conventional restrictions / expectations on models where registers are given on a frame-by-frame basis. The schema should present the TraceRegisterContainer as the same object or a successor to TraceStackFrame, which must in turn be a successor to TraceStack. The frame level (an index) must be in the path from stack to frame. There can be no wildcards between the frame and the register container. For example, the container for Threads[1] may be Threads[1].Stack[n].Registers, where n is the frame level. Threads[1].Stack would have the TraceStack interface, Threads[1].Stack[0] would have the TraceStackFrame interface, and Threads[1].Stack[0].Registers would have the TraceRegisterContainer interface. Note it is not sufficient for TraceRegisterContainer to be a successor of TraceStack with a single index between. There must be an intervening TraceStackFrame, and the frame level (index) must precede it.

      Parameters:
      frameLevel - the frame level. May be ignored if not applicable
      path - the path of the seed object relative to the root
      Returns:
      the filter where the register container should be found, possibly PathFilter.NONE
    • computeFrameLevel

      default int computeFrameLevel(KeyPath path)
      Compute the frame level of the object at the given path relative to this schema

      If there is no TraceStackFrame in the path, this will return 0 since it is not applicable to the object. If there is a stack frame in the path, this will examine its ancestry, up to and excluding the TraceStack for an index. If there isn't a stack in the path, it is assumed to be an ancestor of this schema, meaning the examination will exhaust the ancestry provided in the path. If no index is found, an exception is thrown, because the frame level is applicable, but couldn't be computed from the path given. In that case, the client should include more ancestry in the path. Ideally, this is invoked relative to the root schema.

      Parameters:
      path - the path
      Returns:
      the frame level, or 0 if not applicable
      Throws:
      IllegalArgumentException - if frame level is applicable but not given in the path
    • isAssignableFrom

      default boolean isAssignableFrom(TraceObjectSchema that)
      Check if this schema can accept a value of the given other schema

      This works analogously to Class.isAssignableFrom(Class), except that schemas are quite a bit less flexible. Only PrimitiveTraceObjectSchema.ANY and PrimitiveTraceObjectSchema.OBJECT can accept anything other than exactly themselves.

      Parameters:
      that -
      Returns:
      true if an object of that schema can be assigned to this schema.