Design of a Tool for Checking API Usage in Eclipse-based Components

Jim des Rivieres
Last updated Jan 14, 2005

This document contains the design for a tool for checking API usage in Eclipse-based components and products.

Components

For the purposes of this tool, a component is a set of plug-ins and plug-in fragments. Components must be disjoint, in that the plug-ins and fragments within one component must be distinct from those in other components. In other words, components partition the set of plug-ins and plug-in fragments into disjoint subsets. We also allow a form of library component containing  no plug-ins or fragments; library components are used to describe class libraries such as J2SE that are not located inside any plug-in.

The code for a component is the sum total of the code in the component's plug-ins and fragments (as described by the plug-in manifest). The code is in the form of Java types (predominantly classes and interfaces; J2SE 5.0 adds enumerations and annotation types). The types that are intended to be used by other components are called the component's API, and referred to as API types. A component can provide any number of API types, including none. Typically, only a subset of a component's types are part of its API. The rest are referred to as internal types. Non-public types are always considered internal.

It's common to use Java packages to separate one component from another, and to separate API from internal within a component. Each component generally declares its code in packages different from those used by any other component. An API package is one that contains at least one API type. An internal package is one that contains no API types. The default assumption is that all public top-level types in an API package are API types.

An API type declared in one component can be used by other components is various ways:

It is common for an API type to be intended to be used only in certain ways. In some cases, Java modifiers can be used to rule out unintended uses (e.g., prevent subclassing by declaring the class "final"). But in many cases, the intended uses are spelled out in the type's specification (doc comment). The most common restrictions are:

Types have members (fields, methods, types). For the present, we make the simplifying assumption that all public and protected members of an API type are intended to be used by other components.

Component description files

Each component will be described via a component description file. The file format is XML-based; the DTD follows:

<!ELEMENT component (plugin* package* component-depends)>
<!ATTLIST component 
  name               CDATA #REQUIRED
>
The <component> element provides information about a component. Attributes:

Child elements or the <component> element describe the set of plug-ins and fragments making up the component, provide information about the Java packages and types in the component's code, and include a list of other components on which this component may depend.

Each <plugin> element must identify a distinct plug-in or fragment. The list of plug-ins must be complete; that is, a component contains a plug-in (or fragment) if and only if a <plugin> element occurs as a child of the <component> element.

<!ELEMENT plugin EMPTY> 
<!ATTLIST plugin 
  id                 CDATA #REQUIRED
  fragment           ("true" | "false") "false"
>
The <plugin> element identifies a plug-in or plug-in fragment that is part of the component. Attributes:

Each <plugin> element must identify a distinct plug-in or fragment. The list of plug-ins must be complete; that is, a component contains a plug-in (or fragment) if and only if a <plugin> element occurs as a child of the <component> element.

<!ELEMENT package (type*)>
<!ATTLIST package 
  name               CDATA #REQUIRED
  exclusive          ("true" | "false") "true"
  api                ("true" | "false") "true"

>
The <package> element provides information about a package as used by the component. Attributes:

Each <package> element must identify a distinct package relative to that component. If the unusual case where a package is shared with other components, the <package> element is understood to apply only to the types the component actually declares, and has no bearing on the types declared in the same package in any other component. The list of packages may be incomplete; if the component contains code in a package not mentioned in the list, the package is considered to be internal (equivalent to being explicitly described as <package name="..." api="false" />). The children of the <package> element provide information about specific types in the package.

<!ELEMENT type EMPTY> 
<!ATTLIST type 
  name               CDATA #REQUIRED
  reference          ("true" | "false") "true"

  implement          ("true" | "false") "true"
  subclass           ("true" | "false") "true"

  instantiate        ("true" | "false") "true"
>
The <type> element provides information about a top-level type in a package. Attributes:

(Note: We could extend the schema in the future to allow <type> elements to provide analogous information about their members. We could also extend the <component> element to allow aspects other than code API to be described.)

<!ELEMENT component-depends (component-ref*)>
<!ATTLIST component-depends 
  unrestricted       ("true" | "false") "false"
>
<!ELEMENT component-ref EMPTY>
<!ATTLIST component-ref 
  name               CDATA #REQUIRED
>
The <component-depends> element identifies other components on which this component is allowed to depend. Attributes: If a component specifies <component-depends unrestricted="true">, then it is allowed to depend on any component (and the children, if any, are ignored). Otherwise, the <component-ref> elements give the names of the component on which this component may depend. The <component-ref> element identifies a component by name. Attributes:

Example

A component description of one of the Eclipse Platform components:

	<component name="Eclipse Platform Core Resources">

 <plugin id="org.eclipse.core.resources" />
 <plugin id="org.eclipse.core.resources.win32" fragment="true" />
 <plugin id="org.eclipse.core.resources.linux" fragment="true" />

 <plugin id="org.eclipse.core.resources.hpux" fragment="true" />
 <plugin id="org.eclipse.core.resources.macosx" fragment="true" />

 <plugin id="org.eclipse.core.resources.qnx" fragment="true" />

 <package name="org.eclipse.core.resources">
   <type name="ICommand" implement="false" />

   <type name="IContainer" implement="false" />
   <type name="IFile" implement="false" />

   <type name="IFileState" implement="false" />
   <type name="IFolder" implement="false" />

   <type name="IMarker" implement="false" />
   <type name="IMarkerDelta" implement="false" />

   <type name="IPathVariableChangeEvent" implement="false" />
   <type name="IPathVariableManager" implement="false" />

   <type name="IProject" implement="false" />
   <type name="IProjectDescription" implement="false" />

   <type name="IProjectNatureDescriptor" implement="false" />
   <type name="IResource" implement="false" />

   <type name="IResourceChangeEvent" implement="false" />
   <type name="IResourceDelta" implement="false" />

   <type name="IResourceProxy" implement="false" />
   <type name="IResourceRuleFactory" implement="false" />

   <type name="IResourceStatus" implement="false" />
   <type name="ISaveContext" implement="false" />

   <type name="ISavedState" implement="false" />
   <type name="ISynchronizer" implement="false" />

   <type name="IWorkspace" implement="false" />
   <type name="IWorkspaceDescription" implement="false" />

   <type name="IWorkspaceRoot" implement="false" />
   <type name="ResourcesPlugin" subclass="false" instantiate="false" />

 </package>
 <package name="org.eclipse.core.resources.mapping">
 </package>
 <package name="org.eclipse.core.resources.refresh">
   <type name="IRefreshResult" implement="false" />

 </package>
 <package name="org.eclipse.core.resources.team">
   <type name="IResourceTree" implement="false" />
 </package>

 <component-depends>
    <component-ref name="Eclipse Platform Core Resources" />
    <component-ref name="J2SE" />
 </component-depends>

</component>

How component descriptions are used

Change history