An Empirical Study of Cycles among Classes in Java - CiteSeerX

3 downloads 0 Views 350KB Size Report
The initial step we took in compiling our corpus was to amalga- mate corpora used in other ...... API to access Microsoft format files. Apache. O. N. F1-3.2. 2684. F.
An Empirical Study of Cycles among Classes in Java Hayden Melton, Ewan Tempero Department of Computer Science University of Auckland Auckland, New Zealand {hayden|ewan}@cs.auckland.ac.nz

ABSTRACT Many authors have implied dependency cycles are widespread among the classes of object-oriented software systems. Proponents of the design principle Avoid cyclic dependencies among modules have argued such cycles are detrimental to certain software quality attributes (e.g., understandability, testability, reusability, buildability and maintainability). In this paper we present the first significant empirical study of cycles among the classes of 78 open- and closed-source Java applications. We find that, of the applications comprising enough classes to support such a cycle, around 45% have a cycle involving at least 100 classes and around 10% have a cycle involving at least 1000 classes. We present further empirical evidence to support the contention these cycles are not due to intrinsic interdependencies between particular classes in a domain. Finally, we attempt to gauge the refactoring burden to break all these cycles using the concept of a minimum edge feedback set.

1.

INTRODUCTION

The ultimate goal of software engineering is to produce high quality software systems. It is widely accepted that quality attributes of source code, such as its understandability, cost of maintenance, or predilection to defects, are influenced by the code’s internal structure. It follows that to produce high quality software, we must understand the relationship between quality and internal structure. Our current understanding comes from a large number of design principles (or guidelines, or heuristics) that have been proposed over the years [41, 39, 28, 32]. The proposers of these principles have argued why following them will yield higher quality software than not following them. This raises two fundamental questions: Are these arguments correct, that is, does following these principles produce higher quality software? And, an even simpler question: Are these principles even being followed? In this paper we provide an answer to the latter question for one design principle — avoid cyclic dependencies among modules [38, 28, 32]. It is argued that dependency cycles among modules are detrimental to many important quality attributes (e.g., understandability, testability, reusability, buildability and maintainability). Yet folklore would suggest that dependency cycles are common: many authors have stated [9, 46, 25, 21] [28, p.3] and implied [32, 24,

2, 43, 44, 5] that dependency cycles among classes are common in Object-Oriented (OO) software systems. So how widely is avoid dependency cycles really followed, or more specifically, to what extent do dependency cycles pervade OO software systems? To date the empirical evidence on the extent to which cycles pervade OO systems is somewhat lacking — it rests on differing metrics collected from a handful of mostly small Java [9, 21, 22] and C++ [27] applications. To the best of our knowledge there has been no significant empirical study published of dependency cycles in OO software. The main contribution of this paper is thus a rigorous empirical study of dependency cycles among classes across 78 open- and closed-source Java applications. We say “rigorous” because (1) we have attempted to choose a representative sample of real-world Java applications by selecting applications from differing sources, and of differing sizes and domains; and (2) we have attempted to account for ‘essential’ cycles (i.e., those that may be due to intrinsic interdependencies [28, p.213-215] between objects in the problem domain); and (3) because we have multiple versions of 22 of the 78 applications we have been able to analyse cycles among an application’s classes change over time; and finally (4) we have attempted to gauge the effort to break all cycles among classes using the concept of a Minimum Edge Feedback Set (MEFS). The remainder of this paper is organised as follows. In Section 2 we discuss the ways in which cycles among a program’s organisational units purportedly affect specific quality attributes. In Section 3 we discuss the causes of cycles, the meaning of dependency and the meaning of cycle. In Section 4 we discuss the method by which we conducted our empirical study into the prevalence of cyclic dependencies among Java classes. In particular we describe the Corpus we have developed. In Section 5 we present our results on the prevalence of cyclic dependencies among the classes of Java applications in a software corpus. In Section 6 we present a metric to help estimate the refactoring burden of a cycle. In Section 7 we discuss the significance of our results. In Section 8 we discuss other work that has looked at the prevalence of dependency cycles, as well as tools and language support for avoiding cycles. Finally we discuss future directions for this work and summarise the implications of our results in Section 9.

2. EFFECTS ON QUALITY Permission to make digital or hard copies of all or part of this work for personal or classroom use is granted without fee provided that copies are not made or distributed for profit or commercial advantage and that copies bear this notice and the full citation on the first page. To copy otherwise, to republish, to post on servers or to redistribute to lists, requires prior specific permission and/or a fee. Copyright 200X ACM X-XXXXX-XX-X/XX/XX ...$5.00.

Directly [38, 28, 32, 16] or indirectly [39, 7, 41], many authors have referred to the design principle avoid dependency cycles. To the best of our knowledge the first to discuss it was Parnas, using the phrase loops in the “uses” relation [38]. Parnas states that while reusing code by calling functions declared in other modules is generally considered beneficial, there is potentially a price to be paid for this reuse — “Unless some restraint is exercised, one may end up with a system in which nothing works until everything works”.

He elaborates on this comment by discussing the development of a (presumably) hypothetical operating system where the task scheduler and file system modules have a mutual dependency one another’s routines. The consequence of this, according to Parnas, is that neither can be tested until both are “present and working”. From these quotes we may infer that Parnas was claiming cyclic dependencies can be detrimental to the external quality attributes of buildability and testability. Other design principles also have the effect of avoiding (or reducing) dependency cycles. Stevens et. al. state the design principle minimise coupling between modules [41]. A design with dependency cycles has higher coupling than its acyclic analog (e.g., if modules A and B are in a cycle then B has higher coupling than if the only dependency is from A on B). Riel states “Derived classes must have knowledge of their base class by definition, but base classes should not know anything about their derived classes” [39, p.81]. Disallowing the dependency of a base classes on its derived classes prevents a dependency cycle between the base and derived classes. Riel also states that “In applications that consist of an object-oriented model interacting with a user interface, the model should never be dependent on the interface” [39, p.36]. This has the effect of eliminating cycles between the model and user interface because the user interface normally depends on the model in any case. Finally Martin [31] quotes Booch [7] as saying “. . . all well structured object-oriented architectures have clearly defined layers”. Long dependency cycles have the potential to encompass several layers. When this is the case layers are not clearly defined because a layer should only depend on the layers beneath it.

2.1 Cycles among Classes The most comprehensive work on cyclic dependencies in the context of the object-oriented paradigm is by Lakos [28]. Lakos states that cyclic dependencies among the components of a C++ program inhibit understanding, testing and reuse [28, p.185]. We will see in Section 3 that Lakos’ notion of a C++ component is roughly equivalent to a .java file in Java. Since there is typically one top-level class per source file in Java we will, for the purposes of this section, discuss cycles in terms of (top-level Java) classes. The arguments for cyclic dependencies among classes being detrimental to understanding, testing and reuse are as follows: Understanding. When we look at a class’ source code we want to be able to understand as much of it as possible in isolation, without having to look at the source code of other classes in the system. This is the entire point of modularisation — we can divide a complex system up into simpler modules (or classes) that can be independently developed, maintained and understood [37]. Sometimes, however, it is difficult to understand a class in isolation. When we cannot understand a class in isolation it is often the case that we look at the source code of the classes it depends on (e.g., calls methods on). If we restrict ourselves to understanding a class based only upon the classes it depends on then cycles make understanding a system’s classes more difficult. Consider the two systems depicted in Figure 1 in order to illustrate this. Both systems have two classes, X and Y, but differing dependencies (represented by arrows). In order to understand class X in 1(a) we (at most) need to examine the source code of X and Y. In order to understand Y we at most need only examine the source code of Y itself. Consider now the system of Figure 1(b). Here both classes are cyclically dependent. Unlike in Figure 1(a) where the worst case for Y is examining the source code of Y itself, the worst case for Y has become examining the source code of X and Y. So the argument is that understanding the average class of the system depicted in Figure 1(b) is more work, and therefore more difficult than the

Figure 1: Classes in two, small, hypothetical systems

Figure 2: Classes in two, larger, hypothetical systems

average class in the system depicted in 1(a). Imagine now our strategy for understanding a class is transitive and there are more classes in the system and a much larger cycle. In order to demonstrate what we mean by transitive consider class K of the system depicted in Figure 2(a). In order to understand K we may also have to look at the source code of L. But then to understand L we may also have to look at the source code of M. Since M depends on nothing we should always be able to understand its source code in isolation. So in Figure 2(a) the worst case for understanding class I involves looking at the source code for itself and for J, K, L, M. For class J the worst case is looking at the source code of itself and classes K, L and M. The process continues similarly for classes K, L and M. So we may conclude that the average ‘worst case’ for understanding a random class from the system of Figure 2(a) involves looking at the source code of 3 classes (=(5+4+3+2+1)/5). Consider now the system of Figure 2(b). Since all five classes in this system are involved in one big cycle, we may have to examine the source code in all of the classes in the cycle in order to understand any one class. Clearly this is worse than the average worst case of Figure 2(a). Lakos and Fowler have both argued that cycles are detrimental to understanding but in a less precise fashion than what we have presented above. Lakos states: “. . . you have probably been in a situation where you were looking at a software system for the first time and you could not seem to find a reasonable starting point or a piece of the system that made sense on its own. Not being able to understand or use any part of a system independently is a symptom of a cyclically dependent design [28, p.3]. Fowler, for the moment ignoring the fact he is referring to cycles among packages, makes a statement of similar sentiment: “Such systems [those with cyclic dependencies] are harder to understand because you have to go around the cycle many times” [16]. Testing. We need to be able to test a software system in order to demonstrate its faults. There are strong arguments that cyclic dependencies among classes inhibit unit testing [28, p.161-174] [5, p.348] and integration testing [28, p.174-187] [9, 26, 21] [5, p.983984]. Unit testing is about testing a class in isolation. If two classes are cyclically dependent then it is impossible to test one without the other [28, p.161-174]. In other words, if there are many cyclically dependent classes in a system none of the classes in the cycle can be tested in isolation. Binder says “A small cluster [of classes] can also result from cyclic dependencies (where each class uses the other’s) so that the classes cannot be tested in isolation” [5, p.348]. Cyclic dependencies among classes impede integration testing by preventing a topological ordering of classes that can be used as a test order [9, 26, 28]. Many researchers have dealt with the problem of cyclic dependencies among classes in integration testing by breaking cycles through the creation of stubs [9, 26] [5, p.983-984].

Binder notes that this is problematic because the time and cost to develop stubs can be as high as the cost to develop the component that is being stubbed [5, p.983]. Lakos advocates the elimination of cycles from the design rather than stubbing [28, ch.4]. Binder [5, p.984] states that Thuy [43, 44] and Barbey [2] also advocate the elimination of cycles from the design of a system. The understandability of a system also affects our ability to test it. If testability is defined as the ease at which software can be made to demonstrate faults through testing [3, p.88], and code inspections are considered a testing activity, then systems with poor understandability are more difficult test. Poor understandability makes code inspections more difficult and time-consuming to perform. The consideration of code inspections as a testing activity may seem odd to some but it is concordant with the IEEE Standard Glossary of Software Engineering Terminology definition of testing: “. . . The process of analyzing a software item to detect the differences between existing and required conditions (that is, bugs)” [23]. Reuse. There are many forms of reuse. We can reuse code or documentation, or more conceptual entities such as architectural patterns [42, p.3-4]. The form of reuse that is inhibited by cyclic dependencies among classes is code reuse. In fact it is a very specific specific form of code reuse that is worsened by cyclic dependencies. The form of code reuse to which we refer does not involve what Martin terms code copying [32]. Code reuse, as the phrase is used by Martin [32] and Lakos [28], involves copying source files (and the libraries that they depend on) from one program to another, without (1) having to modify the textual content of these source files in order to make them compile in the new environment and (2) introduce stubs for classes on which they depend. Code copying on the other hand is where chunks of text are copied from one program to another (e.g. a method declaration or part of a class), or entire source files are copied but they have to be modified in order to successfully compile in the context of the new program. The modification of source code performed in code copying is undesirable because it usually means that (1) the copied code eventually diverges so much from the original that it becomes unrecognisable; (2) the copier becomes the owner of the copied code and is intimately responsible for maintaining it; and (3) if the original code is enhanced in its origin system it is no longer possible to easily integrate new versions of it. If however the code had been “properly” reused we could easily integrate it into our system following an enhancement of it in the origin system [32]. Consider the case where several classes are cyclically dependent. In order to reuse them (deploy them in the context of another system through code reuse) we must deploy all of them, regardless of whether the functionality we actually need is implemented entirely in one (or a small subset) of the classes. While the sheer number of the classes copied is of concern because it increases the amount of code in the system that needs to be understood and can contain bugs, it is not the main problem. Rather the problem is that many of the classes are not likely to be strictly necessary for the given class to provide the functionality we want from it, or as Lakos [28, p.14] states “in order for a . . . subsystem to be reused successfully, it must not be tied to a large block of unnecessary code”. Parnas also implies that cyclic dependencies inhibit code reuse in his discussion of “hierarchical structure”, though he does not use the terms cycle, loop or circular: “The existence of the hierarchical structure assures us that we can ‘prune’ off the upper levels of the tree and start a new tree on the old trunk. If we had designed a system in which the ‘low level’ modules made some use of the ‘high level’ modules, we would not have the hierarchy, we would

find it much harder to remove portions of the system, and ‘level’ would not have much meaning in the system” [37]. Indeed, by definition, high-level modules depend on low-level modules. If, as Parnas states, low-level modules depended on high-level modules then we would effectively have a dependency cycle.

2.2 Cycles among Packages The package construct is a feature of some programming languages (such as Java, Ada and C++) that allow classes to be grouped together. In this way the subsystems that comprise a program can be reflected in the program’s source code. In large-scale software systems, comprising thousands of classes, subsystems are absolutely essential [28, 6, 11, 32]. They help us to avoid information overload — a result of the limits on the human mind’s informationprocessing capacity [11]. They facilitate a vocabulary that developers of a system can use in communication [11, 6]. They allow managers to determine a partial ordering of development activities with respect to time, that allows for parallelism in development effort [36, 28, 32]. Finally, they have a significant impact on the system’s quality particularly with respect to reusability and testability [28, 32, 42]. Dependency cycles among classes can cause dependency cycles among packages. In order to understand how this can be consider one desirable property of a package — that it is of manageable size [11, p.107] [28, p.481] [36, p.51]. Meyer states that for a package to be of manageable size it should contain 5-40 classes and, more fundamentally, it should be able to be developed by 1-4 people and entirely understood by a single person [36, p.51]. We will also express an upper limit on package size in terms of number of classes it contains, N . So any given package in a system should not exceed N classes. Now imagine there is a cycle involving N +1 classes — this means that there must be a cycle among at least two packages since the first N classes in this cycle could all feasibly belong to the same package but the remaining 1 class must belong to another package. Indeed we can use this pigeon hole principle-style counting argument to give lower bounds on the number of packages that must be involved in a cycle when there is a cycle among classes of a given length [33]. It is worth noting however that a cycle among packages does not necessarily mean there is a cycle among classes. Lakos [28, p.494], Martin [32] and Fowler [16] state that there should be no dependency cycles among the packages of an application. The reasons dependency cycles among packages should be avoided are as follows. Production. Cyclic dependencies inhibit concurrent development of a system’s packages, each of which may be maintained by a separate group of developers. As Martin states, in the presence of cycles among packages, “It is not uncommon for weeks to go by without being able to build a stable version of the project. Instead, everyone keeps on changing and changing their code trying to make it work with the last changes that someone else made” [32]. Fowler also states “Cycles are problematic, because they indicate that you can get in a situation in which every change breeds other changes that come back to the original package” [16]. Lakos states that cyclic dependencies prevent staged releases of packages [28, p.495]. Staged releases are desirable because they allow packages in lower layers to be tested while packages in higher layers are still being developed [28, p.512-514]. Staged releases can thus serve to improve the reliability of a system [28, p.495]. Marketing. Lakos states “Often a system will have a basic functionality and several optional add-on packages of functionality” [28, p.494]. In this way the basic version of a system can be sold relatively cheaply and customers can buy add-on packages as required. If there are cyclic dependencies between the add-on and

‘core’ packages then the add-ons cannot be shipped and marketed as truly separate options. This is of particular importance when the add-ons are developed by third parties (e.g., Eclipse plugins). Imagine if the Eclipse IDE depended on third party plugins — this would increase its size, could decrease its reliability if these plugins had bugs. It could cause integration problems if a newer version of a plugin was released subsequent to an Eclipse release. Development. In the context of C++ cyclic dependencies among packages can inhibit the day-to-day development of a system [28, p.494]. If the package structure is cyclic then in order to compile the classes of one package we will usually have to recompile all the classes in cyclically dependent packages. This is more timeconsuming than if the package structure is hierarchical. In a hierarchical package structure we need only recompile all the packages that transitively depend on the one whose classes have been changed [32]. In Java this is not so much of a problem because a Java .class file does not depend on the layout of other .class files — rather it depends on the names that appear in the other class files’ constant pools [30, ch.4]. Usability. Lakos states that “. . . users will not want to have to link-in a huge library or several large libraries just to use some simple functionality of the basic system” [28, p.495]. If packages are considered the unit of deployment for reuse [32] then cyclic dependencies among packages suffer from the same reuse problems (that have been previously discussed) as cyclic dependencies among classes. We do not want a package to be tied to a bunch of seemingly unrelated packages.

3.

CYCLIC DEPENDENCIES

So far our discussion has been about the effect of dependency cycles on quality, yet we have not been specific about the meaning of cycle, or for that matter, the meaning of dependency. In this section we formalise our notion of dependency and cycle. We also discuss some of the causes of cycles in order to argue that some cycles are “necessary” while others are “unnecessary”.

3.1 Meaning of Dependency Dependencies between source files are a natural consequence of modularisation. In dividing a program up into modules we break it up into more manageable parts. But these parts must collaborate in order to provide the functionality of the system as a whole. It is these collaborations that cause dependencies. In this paper we are interested in the meaning of dependency as it applies to cyclic dependencies among the Java analogs of Lakos’ C++ components. Up until now we have referred to these analogs as top-level Java classes. Since Lakos defines dependency in terms of components, and our work largely derives from his, we must also at this point justify our view of these components in the context Java. Lakos defines component in the context of C++ as follows: “Structurally a component is an indivisible physical unit, none of whose parts can be used independently of the others. The physical form of a component is standard and independent of its content. A component consists of exactly one header (.h) file and one implementation (.c) file” [28, p.100]. We can think of Lakos’ component in the context of Java as a single .java file. There is no need to consider pairs of files because in Java there is no equivalent to a header file — the published interface of a type, through which its clients can manipulate it, resides in the same file as its implementation. Also, since there is typically one top-level type declared per .java file we can refer to Lakos’ component in the context of Java as a top-level type (including all of the inner types it lexically encloses). We will refer to this top-level type (which may be an interface-type, enum-type of

class-type) and all the types it lexically encloses simply as a class. It turns out that the latter definition (of a top-level class) is preferable for this paper because it does not take into account the import statements that appear before any type declarations in a .java file. These import statements can be problematic if they refer to types that do not appear in the body of any type declarations in the .java file. We call such import statements ‘redundant imports’ because they can be removed from a .java file without effect on any declared types. Indeed in the compiled .class files for the types declared in the .java files there are no references to types referred to by redundant imports. Also there is already good tool support available for removing these ‘superficial’ dependencies (e.g., Eclipse). So whether we refer to Lakos’ notion of a component in the context of Java as a .java file or a class (meaning a top-level class including all its lexically-enclosed inner classes) in the following text we are ignoring redundant imports. Now we have argued the entities to which our dependency relation applies are Java “classes”. The dependency in which we are interested is a “static” dependency (c.f. “dynamic” or runtime dependency) defined by Lakos for C++ as follows: “A component y DependsOn a component x if x is needed in order to compile or link y” [28, p.121]. In the next subsection we will introduce some dependency relations for Java and explore the applicability of this definition of Lakos’ DependsOn to Java.

3.1.1 Dependency Relations in Java The dependency relationships for Java that are applicable to this paper are presented below. These relationships only consider types appearing in a systems source code — not types in external libraries (such as the Java API). We exclude types that are not declared in a system’s source code because the developers of that system have no control over cycles among these types. Note that there can be no dependency (and thus no cycle) from types declared in external libraries to types declared in the application’s source code if these libraries are truly external. Note also that the nomenclature for the relationships comes from Lakos [28, p.247-256]. For a Java source file A: •

USES(A) is the set of all .java files that declare types that A refers to in its text (recalling we exclude those types that are referred to by redundant imports). In other words, it is the set of all Java source files that A DependsOn. • USES - IN - SIZE (A) is the set of all .java files that declare types on which methods are called and fields are accessed in the text of A. Constructor invocations are considered as method calls and supertypes of types declared in A are also included in this set. This is a set related to that used to compute the Chidamber and Kemerer CBO metric [10, 8]. • USES - IN - THE - INTERFACE (A) is the set of all .java files that declare types that appear in the interface of A. By interface we mean the methods and fields that A declares that are accessible by other classes i.e, the non-private fields and methods. So this set includes types that appear in the return type, formal arguments and thrown exceptions of non-private methods, and the declared types of non-private fields. We also includes the direct supertypes of the class so that the transitive form of USES - IN - THE - INTERFACE includes all the methods and fields that can be called on it (even those types that appear solely in supertypes). • USES - IN - NAME - ONLY(A) = USES(A) \ USES - IN - SIZE (A). We will explain the significance of this relation shortly.

We show the computation of the above relations for the following source file. Note that List, Iterator and Object correspond

to classes in the Java API so do not appear in any of the relations’ sets. All other types are assumed to be declared in the application’s source files. //MyObject.java public class MyObject extends MyParent { FastSetInterface set = new MyFastSet(); public List toList(MyName name) { MyFastList myl = new MyFastList(); myl.add(name); for(Iterator it = set.iterator(); it.hasNext();) { Object o = it.next(); myl.add(o); } return myl; } } USES(MyObject) = {MyParent, MyFastSetInterface, MyFastSet, MyName, MyFastList} USES - IN - SIZE (MyObject) = {MyParent, MyFastSetInterface, MyFastSet, MyFastList} USES - IN - NAME - ONLY (MyObject) = {MyName} USES - IN - THE - INTERFACE(MyObject) = {MyParent, MyName}

So why do we need all these different dependency relations for Java when the single DependsOn relation is adequate for C++? The main reason is that the DependsOn or USES relation alone is inadequate in the context of Java because it does not allow what Lakos [28, p.213] terms intrinsic interdependency to be expressed in a type-safe fashion. Dependency cycles due to intrinsic interdependency can be thought of as “necessary” cycles. Cycles not due to intrinsic interdependency can be thought of as “bad” or “unnecessary” cycles. So we need the other relations in order to distinguish “necessary” cycles from “bad” cycles in Java. Intrinsic interdependency is when there is “Inherent coupling in the interface of related abstractions” [28, p.213]. This dependency is intrinsic because it cannot be sensibly avoided. Lakos illustrates with a graph modeled with Node and Edge classes. Conceptually an edge exists to connect a source node to a destination node. It is therefore likely that a client of the Edge class would be interested in the nodes this edge connects. Thus Edge provides methods getSrcNode and getDstNode that cause a Edge to depend on Node. Clients of Node are likely to have an analogous requirement meaning Node depends on Edge. Thus the conceptual relationships between edges and nodes in a graph have caused Edge and Node to be cyclically dependent. In order to see why DependsOn does not discriminate cycles due to intrinsic interdependencies in Java consider the code below: //Parent.java public class Parent { private Child[] children; public Child[] getChildren() { return children; } } //Child.java public class Child { private Parent parent; private String name; public Child(String n, Parent p) { name = n; parent = p; } public Parent getParent() {

return parent; } }

In the above code assume that there is an intrinsic interdependency between Parent and Child. That is, clients of a Child object need access to its Parent object; and clients of a Parent object access to its Child objects. In this Java code there is a cyclic dependency between Child and Parent when the dependency relation considered is DependsOn (or, equivalently, USES). In order to compile Parent.java we need Child.java on the classpath because the type Child is referred to in its text. Similarly Child.java requires Parent.java for its compilation. One way to express this intrinsic interdependency in Java without inducing a cyclic dependency is to replace Child.java with Child2.java (below). If Parent is changed to make reference to Child2 instead of Child then there is no cyclic dependency because Child2.java makes no reference in its text to any types declared in Parent.java. However, Child2 has sacrificed type-safeness to do so, and the burden of casting getParent() from Object to Parent has been placed on Child2’s clients. //Child2.java public class Child2 { private Object parent; private String name; public Child2(String n, Object p) { name = n; parent = p; } public Object getParent() { return parent; } }

We need to be able to express cycles due to intrinsic interdependency without inhibiting the quality attributes we have previously discussed. Lakos claims that a class can be tested, understood and reused independently from any types it (transitively) uses in-nameonly (i.e., appearing in the USES - IN - NAME - ONLY set when the relations from which it derives are first are computed transitively) [28, p.247-256]. Since USES - IN - NAME - ONLY is defined as USES \ USES - IN - SIZE , we are only concerned about cycles in the USES - IN SIZE relation with respect to testing, reuse and understanding. So we should express intrinsic interdependency by referring to a type in-name-only (i.e. not calling any methods on it, or accessing any of its fields). In Child.java (above), Parent is used in-nameonly. Similarly in Parent.java (also above), Child is used in-name-only. For these classes there are no cycles in the USES IN - SIZE relation so intrinsic interdependency is being expressed between these without detriment to their testing, reuse and understanding. The significance of the USES - IN - THE - INTERFACE relation also relates to intrinsic interdependency. Since intrinsic interdependency manifests itself as types appearing in the published (non-private) interfaces of related classes [28, p.247-256] we can use cycles in the USES - IN - THE - INTERFACE as an upper bound for the cycles that we would expect to see due to intrinsic interdependency. It is an upper bound because not all cycles in the USES - IN - THE - INTERFACE relation are necessarily due to intrinsic interdependency — it may be the case that a type appears in the interface of a class when conceptually it should not (e.g., a class from the model of an application has a method with a return type of a class declared in the view of the application — recall an application’s model should not depend on its view [39]). Indeed the reason a type appears in the published interface of a class when it conceptually should not could be

due to other causes of cyclic dependencies identified by Lakos: enhancements to a program over time that are not-well-thought-out, or enhancements that have been made for a programmer’s convenience (e.g., creating factory methods for derived types in a base type — recall a base type should have no knowledge of its derived types [39]) [28, p.204-215]. After all this, we still need the USES relationship for Java because it is this relationship that we are interested in from the perspective of packages. So as we previously argued long cycles in the USES relationship cause cycles among packages, and cycles among packages impact their own software quality attributes.

3.1.2 Dependency Relations in C++ If DependsOn is not the relationship we care about with respect to cycles among classes in Java why is it that it is the relation we care about in C++? The answer is to do with a subtle difference between Java’s import- and C++ include-statement. Consider the type-safe Java Child class described earlier translated into a C++ implementation file: //child.c #include "child.h" //#include "parent.h" -- not needed class Parent; //-- NOTE! Parent *parent; String *name; Child::Child(String *n, Parent *p) { name = n; parent = p; } Parent *Child::getParent() { return parent; }

The file child.c can be compiled without Parent’s declaring source file (parent.h) because we have included what is known as a “forward declaration” of a class Parent in the file child.c itself. Note that we can only do this because no methods or fields on Parent are accessed in the text of child.c. Indeed it is the observation that no methods or fields on Parent that lead to the dependency relation USES - IN - SIZE and USES - IN - NAME - ONLY. In child.c, Parent can only be used in-name-only because it is referred to with an opaque pointer. Indeed child.c can be reused in the way described in Section 2.1 without parent.h because of the forward declaration and can be understood independently from parent.h or parent.c because no methods are called on Parent. In Java, even if a type is used in-name-only, it is not possible to reuse its referring type because there are no forward declarations, but the arguments for understanding and testing should still apply.

3.1.3 DependsOn and Java Generics Java Generics (available in Java 5) are allow us to reap the reuse in Java that is afforded by forward declarations in C++. By replacing a reference to a type with a type-parameter we can eliminate cycles in the USES relation and can thus dispense with the more cumbersome USES - IN - SIZE and USES - IN - NAME - ONLY relations. We illustrate the use of generics for this purpose with the code below for Edge (Recall we used Edge and Node earlier used to help explain intrinsic interdependency). //Edge.java public class Edge { private K src; private K dst; public K getDst() { return dst;

Figure 3: Graph with SCCs

} public void setDst(K dst) { this.dst = dst; } //... }

We can apply the same technique to Node thus eliminating its dependency on Edge. By doing this Node can be reused independently of Edge and vice versa. This technique is not discussed by Lakos but is reminiscent of work done by VanHilst et. al. using C++ templates to implement roles [45].

3.2 Meaning of Cycle There are many types of cycles — a simple cycle for example is a path with no repeated vertices that starts and ends on the same vertex. The type of cycle that is applicable to our study is that of a Strongly Connected Component (SCC) or strong component. The notion of a SCC extends the notion of mutual reachability. “A strong component of a digraph G is a maximal strongly connected subdigraph of G. Equivalently, a strong component is a subdigraph induced on a maximal set of mutually reachable vertices” [19, p.128]. We are interested in SCCs because all classes in a SCC are cyclically dependent — every one is reachable from every other one. It is worth noting that we do not ignore simple cycles because an SCC must comprise at least one simple cycle, but in our data it usually comprises many simple cycles interacting in complex, intertwined ways. Consider the directed graph of Figure 3. The vertex sets for the SCCs comprising more than 1 vertex are: {A, B, C, D} and {F, G} and {H, I, J}. For the analysis performed in this paper we represent .java source files as vertices and one of the USES, USES - IN - SIZE and USES - IN - NAME - ONLY relations as edges.

4. METHODOLOGY In order to determine the prevalence of cyclic dependencies among classes in Java software we compiled1 a software corpus and built tools to infer dependencies between Java classes. In this section we describe our strategy for selecting applications for the corpus and argue that our corpus is a representative sample of the Java software that is in the world today. We also describe how the tools we used to collect dependency data work so that others wishing to replicate our results may do so.

4.1 Corpus In the general sense a corpus is “a large collection of writings of a specific kind or on a specific subject” or “a collection of writings or recorded remarks used for linguistic analysis” [1]. The software corpus referred to in this paper2 is a collection of Java applications that has been used for static program analysis. Since we are interested in determining the prevalence of cyclic dependencies in real 1 As in the general meaning of the word “compiled”: to put together materials from various sources. 2 A large subset of the corpus that appears in this paper has been used elsewhere [4].

software the programs in our corpus need to be a representative sample of Java software that is in the world today. We selected programs for our corpus along several dimensions: application domain, size, origin and open/closed source. The applications in our corpus are listed in along these lines in the Appendix. In this table the ‘Application’ column gives the name (and latest version we have) of an application. The ‘#Classes’ column gives the size of the application in terms of number of .java source files. In the ‘Description’ column we have identified the application’s domain. The ‘Origin’ column cites the source from which we obtained the application as either a company3 or website. The ‘O/C’ column indicates whether the application is open or closed source. The ‘V’ column indicates whether we have multiple versions of the application in the corpus — this is a distinguishing feature of our corpus, we have multiple versions of 22 of the 78 applications in it. Finally the ‘Notes’ column gives any other details we consider relevant — particularly we note if the application was donated to the open source community by a company. The initial step we took in compiling our corpus was to amalgamate corpora used in other published papers. Two papers we found that made use of corpora were an empirical study of type confinement [20] and a study of “micro patterns” [17]. All accessible applications from these corpora were added to our corpus. Further applications were then added to the corpus based on software that we were familiar with (e.g. Azureus, ArgoUML, Eclipse, NetBeans). Finally we identified popular (widely downloaded) and actively developed open-source Java applications from various websites, including: developerWorks4 , SourceForge5 , Freshmeat6 , Java.net7 , Open Source Software In Java8 and The Apache Software Foundation9 .

4.2 Tools We built two tools to infer dependencies between Java classes. One operates on Java source code and the other on byte code. The tool that operates on source code is called Jepends and is described in an earlier paper [34]. It was used to collect the data in applications that originated from company B. At the time Jepends was used to collect this data it was unable to compute the USES - IN - SIZE relation so this data is missing for the applications from company B. The second tool we developed operates on Java bytecode. It is built on top of the Byte Code Engineering Library (BCEL) library10 . Using BCEL, each of the dependency relations can be inferred from a .class file relatively easily. This is largely due to the format of a Java class file. As stated in the Java Virtual Machine Specification all of the symbolic information for a class, including its dependencies, are stored in the class’s constant pool: “Java virtual machine instructions do not rely on the runtime layout of classes, interfaces, class instances, or arrays. Instead, instructions refer to symbolic information in the constant_pool table” [30, ch.4]. 3

Intellectual property agreements mean we cannot identify companies by name. Instead we have identified each company with the letters A-H. In this way it is still possible for the reader to deduce all the applications originating from a particular company. 4 http://www-128.ibm.com/developerworks/views/ java/downloads.jsp 5 http://sourceforge.net/ 6 http://freshmeat.net/ 7 http://community.java.net/projects/ 8 http://java-source.net/ 9 http://apache.org/ 10 http://jakarta.apache.org/bcel/

One issue that our byte code tool has to deal with, that is not applicable to Jepends, is how to discriminate classes declared in an application’s source files from those comprising external libraries the application leverages. We discriminate source classes from libraries classes by specifying a list of Java package names for each application that represent its source classes. For instance, early versions of Eclipse have source classes in the com.ibm package and its subpackages. Later versions of Eclipse have source classes in the org.eclipse package and its subpackages. Our tool implements this package style filtering by checking the prefix of the fully qualified classname. So for Eclipse all we need do to determine if a class is a source class is check it starts with the string “com.ibm.” or “org.eclipse.”.

4.2.1 Computing Dependency Relations from Bytecode Using BCEL it is quite easy to compute each of the dependency relations from a Java .class file. The easiest relation to compute is probably USES - IN - THE - INTERFACE — we simply create a JavaClass for the .class file and determine the declared types of its non-private (i.e., public, protected and default access) fields; and determine the formal argument types, return types and thrown exception types of the non-private methods. Determining USES IN - SIZE is slightly more difficult — we must examine the each of the method’s instructions searching for InvokeInstruction and FieldInstructions. We then determine the class (via Instruction::getClassName) in which the method or field appears and add this to the USES - IN - SIZE set. As per the definition of USES - IN - SIZE we also add the direct supertypes of the original class to its set. In order to compute USES we union USES - IN - SIZE and USES - IN - THE - INTERFACE as well as computing the declared types of any private fields and parameters, return types and thrown exceptions of any private methods. The USES - IN - NAME - ONLY is a derived relation in that it is defined in terms of two other relations USES \ USES - IN - SIZE so we do not need to do anything extra with the byte code in order to compute it. Recall also that we are computing dependencies between .java files (excluding any dependencies due to redundant imports). In order to reconstruct the dependencies of a .java file from its .class files we merge the dependencies of inner classes (which have a ‘$’ in their file name) with the top-level counterparts (whose name precedes the ‘$’ character in the inner classes’ file names). There are some peculiarities of Java bytecode that warrant further discussion. In computing USES - IN - SIZE we said that we examined the code of methods, but this still leaves the initialisers of fields and the static initialiser. In byte code fields are initialised by moving their initialising statements into a constructor. Constructors and the static initialiser are represented as specially-named methods bytecode, and , respectively. Another peculiarity of bytecode is that some type information is thrown away. Particularly the type of local variables does not need to be retained. This is because (1) any overloaded method bindings that are determined by the type of the that local variable (i.e., if the local variable appears as an argument to a method) are done so at compile time, not runtime (this approach actually leads to some issues with binary compatibility when a new overloaded method is added, see 13.2 of the Java Language Specification [18] ); and (2) the local variable is not scoped outside the class’s method so there is no need to retain type information (e.g., for clients of its class) once it has been compiled. Another issue with bytecode is to do with what is referred to as “inconstant constants” [18, ch.13]. These are static final fields of primitive and string types that are copied by value into clients of

x-axis of this plot represents size in number of classes and the yaxis represents the proportion of applications in the corpus that are of at least this size for the top-most data series (“application size”), or have a SCC of at least this size for the bottom-three data series. So as not to bias the results towards any particular application (recall there are multiple versions of 22 of the 78 applications in the corpus) only the latest version of each application is considered in this chart. Thus the chart shows the proportion of applications with cyclic dependencies of growing sizes, and the proportion of applications of growing sizes. We can see from the dependency relation series of Figure 4 that:

Figure 4: Proportion of applications in corpus of growing sizes and with SCCs of growing lengths on normal-log scale. a class. In this way the dependency from a client on the field’s declaring class is lost in the byte code. One final issue with byte code is to do with the way methods and fields are bound to classes. Consider the following code snippet in illustration: MyObject o = MyObject(); o.toString(); In this code snippet there will be an InvokeInstruction in the bytecode that refers to the method toString(). There is an issue as to which class the MethodRef entry in the constant pool will refer — MyObject or java.lang.Object. In actuality it will refer to the class in which toString is declared. So if toString is declared in MyObject it will refer to MyObject, otherwise it will refer to java.lang.Object (assuming MyObject has no other supertypes). So the question is what is the effect of these bytecode peculiarities on our study of dependency cycles? None of these peculiarities affect the USES - IN - THE - INTERFACE relation. The other relations have the potential to be affected, but since we are effectively considering these relations transitively (for the computation of SCCs) the main effect is that the transitive dependency computed from byte code for each of the relations for a class will be less than its actual dependency. This means our results are actually a lower limit on the cycles among classes in the USES - IN - SIZE and USES relation — the cycles, if computed from source code could be even bigger!

5.

RESULTS

In this section we present the data we have collected on cyclic dependencies among the classes of applications in the corpus. We begin with an overview of results showing the proportion of applications in the corpus that have SCCs of growing sizes in the dependency graphs of each of the relations (USES, USES - IN - SIZE and USES - IN - THE - INTERFACE ). We then show the break down of an application’s classes into SCCs of varying sizes (again for each of the relations) for the latest version of each application in the corpus. Finally we show, again using a breakdown of classes into SCCs, how the cyclic dependencies change over an application’s version history for the 22 applications of which we have multiple versions.

5.1 Overview The plot in Figure 4 provides the highest level view of the prevalence of cyclic dependencies among classes in Java software. The

• For the USES - IN - THE - INTERFACE relation about 69% of the applications in the corpus have a SCC of size >10, about 13% have a SCC >100 and no applications have a SCC >1000. In fact, the largest SCC in this relation is 542 classes. • For the USES relation about 85% of the applications in the corpus have a SCC of size >10, about 40% have a SCC >100 and 3% of the applications have a SCC >1000. In fact, the largest SCC in this relation is 2145 classes. • For the USES - IN - SIZE relation about 81% of the applications in the corpus have a SCC of size >10, about 36% have a SCC >100 and 3% of the applications have a SCC >1000. In fact, the largest SCC in this relation is 1909 classes. The distribution of size is shown on the same plot as distribution of SCC sizes because in order for an application to have an SCC (in any of the relations) of a given size, it must comprise at least that number of classes. We can see from Figure 4 that about 92% of the applications comprise at least 100 top-level, source-defined classes, and that 30% of the applications comprise at least 1000 classes. So combining the information size and SCC information we can infer that about 10% (=3%/30%) of applications that are large enough to contain a SCC of size 1000 in the USES or USES - IN - SIZE actually do contain one.

5.2 Snapshot Data In this section we show the breakdown of an application’s classes into SCCs of growing sizes for each of the dependency relations. Again, we do this just for the latest version of each application in the corpus — applications for which we have multiple versions are dealt with in the next section. Each of the bar charts depicted in Figures 5, 6 and 7 can be read as follows. The x-axis represents the application sorted alphabetically by name, and the y-axis represents the number of classes in SCCs. The stacked bars represent the number of classes involved in SCCs of growing sizes (>1000, >500, >100, >50, >20, >1 and >0). Classes in the bar representing ‘>0’ must be in SCCs of size 1, so are not cyclically dependent with any other classes. Consider the bar for Eclipse in the chart of Figure 5. The bottommost bar in Eclipse’s stack corresponds to classes involved in SCCs in the USES relation of size >500. The height of this bottom-most bar is about 700. This means about 700 classes are involved in SCCs of size >500. In fact, we can deduce that the largest SCC in Eclipse must be 700 from this bar because there is no way to split 700 into two parts, both >500 in size. The second bar from the bottom in Eclipse’s stack corresponds to classes involved in SCCs >100 in size. This bar finishes at about 2600 on the y-axis so we can infer there are about this many classes involved in SCCs >100 in size. Additionally we can infer that there are 1900 (=2600-700) classes involved in SCCs >100 and 1 bar finishes (at about 5000 for Eclipse). This means that close to half (=5000/11500) of

Figure 5: SCCs in USES relation over corpus

Figure 6: SCCs in USES - IN - SIZE relation over corpus

Figure 7: SCCs in USES - IN - THE - INTERFACE relation over corpus Eclipse’s classes are involved in cycles in the USES relation. Similar analysis can be performed on any bar-stack in the following charts. Some interesting observations from the chart of Figure 5 are as follows: • Applications C1-5.0.2 and D1-2005 have the largest SCCs in the USES relation — both have bars corresponding to ‘>1000’. D1 must have a SCC of about 1900 (since there is no way to split 1900 into two parts both >1000) and C1 has approximately 2100 involved in SCCs >1000 in size. Though we cannot infer from the graph that C1’s SCC is of size 2100, we checked the raw data its SCC is indeed of this size. • Azureus and Hibernate can be singled out for having a large proportion of their classes involved in a ‘large’ SCC >500 in size. The chart shows that Azureus has about 1700 classes total and about 800 of these classes are involved in a single SCC. Similarly it can be read from the plot that Hibernate comprises about 900 classes and 700 of these are involved in a single SCC. • Eclipse versus Netbeans is an interesting comparison because both of these applications come from the same domain and provide similar functionality. Eclipse has a SCC of size 700 whereas the largest SCC in Netbeans is not >250. Indeed a lower proportion of classes are involved in cycles in Netbeans (2700 out of 8400) than Eclipse (5000 out of 11000). This provides evidence to support the view that cycles are not inherent to particular domains. • Some application have a very small proportion of their classes involved in cycles (e.g., Columba, B3-2.0.0, James, Open Office, B6-2.5.x and B10-2.0.x). This suggests it is possible, in a practical sense, to largely avoid cycles, even in the USES

relation. Figure 6 depicts a chart of the SCCs in the USES - IN - SIZE relation. Although it looks similar to the plot of the USES relation in Figure 5, many of the applications show a reduction (albeit slight) in the number of classes participating in cycles. In some sense this is hardly surprising since USES - IN - SIZE (x) ⊆ USES(x), by its very definition. On the other hand it indicates that some types are not used in-size — rather they are used in-name-only. This means that Lakos’ notion of an opaque pointer [28, p.247-256] is used in real Java code, even if its usage is not deliberate. Some interesting observations from the chart of Figure 6 are as follows: • Hiberate shows a significant difference between the SCCs in the USES and USES - IN - SIZE relations. In the USES relation we observed 700 was the largest SCC yet in the USES - IN SIZE relation we observe that the largest SCC is only around 300 classes. It is tempting to say that this means that types are used in-name-only extensively in Hibernate, however this is not a valid conclusion. It is not valid because one type used in-name-only in one class can be that which breaks a large SCC. • C1-5.0.2 and D1-2005 both still contain a SCCs >1000 in size. Many applications still contain SCCs >500, >100 and >50 in size. Again, we cannot conclude from this that types are not widely used in-name-only. We can conclude from this that types are not used effectively used in-name-only (i.e., to break SCCs in the USES relation in these applications. Figure 7 depicts a chart of the SCCs in the USES - IN - THE - INTERFACE relation. Examination of the SCCs in this relation allow us to determine a rough upper-bound for cycles in a system due to intrinsic

interdependency. If this is a reasonable upper-bound then plot of the SCCs in this relation compared to the charts of SCCs in the USES and USES - IN - SIZE put the design of many of the applications in a very bad light. Some interesting observations from Figure 7 are as follows: • Only one application has SCC in the USES - IN - THE - INTERFACE relation >500 in size (C1-5.0.2). • The largest SCC in the USES - IN - THE - INTERFACE is dramatically smaller than that that of the USES and USES - IN - SIZE relations for most of the applications in the corpus. Consider Eclipse and Netbeans in illustration — the largest SCC in Netbeans is 100 in the USES relation) and the largest in Eclipse is now 500, >100, >50, >20, >1) tend to increase for an application over time. That said, consider B2 and B5: in the middle of B2’s version history the largest SCC dips from >100 to >50 and at the end of B5’s version history dips from >500 to >100. Our discussion with company B revealed that at these points in version history both these products underwent major refactoring/rewrite to improve their internal structure. A consequence of this was reducing the largest SCC. We discuss this further in Section 7.2. • The size of the largest SCC in ArgoUML also decreased between versions. We think that this was due to some refactoring activity. The change history for ArgoUML11 shows that for version 0.17.1 and 0.17.3 (versions between the two versions we have of ArgoUML) that the changes were “Removed deprecated methods” and “Changed persistence mechanism” respectively. Removing deprecated methods certainly has the potential to remove dependencies as does changing the persistence mechanism. • There is also a dip in the total number of classes participating in SCCs in Netbeans from version 3.6 to 4.0. Again we believe that this may be due to a rewrite of the ‘project’ model 11

http://argouml.tigris.org/project_schedule.html

Figure 11: Strength of connections in three SCCs each of size 5. for Netbeans 4.0: “Projects have been completely redesigned in NetBeans IDE 4.0.”12 . • Azureus is also worthy of mention because unlike Eclipse, where enhancements tend to increase the the number of classes participating in cycles of each of the SCC groups (i.e., >500, >100, >50 etc), in Azureus the trend is that classes attach themselves to largest SCC causing it to grow. This large SCC has a greater potential to affect package structure than many small SCCs (see Section 2.2). The plot of Figure 9 that shows how cycles in the USES - IN - SIZE relation change over time. The trends for the cycles in this plot mirror those in the USES plot of Figure 8. Consider the chart of Figure 10 that shows how cycles in the USES - IN - THE - INTERFACE relation change over time. One interesting thing to note from this plot is that there is also a dip in the total number of classes participating in SCCs in Netbeans from version 3.6 to 4.0 (as per the USES relation). There are however no discernable dips in this relation’s SCCs corresponding to those dips in the USES relation’s SCC for applications B2, B5 and ArgoUML.

6. REFACTORING In the previous section we saw that large SCCs in the USES and USES - IN - SIZE relation are common in many Java applications. In this section we attempt to determine how strongly connected the classes in an SCC are to one another. We do this in order to gauge the amount of work required to turn the SCC into a Directed Acyclic Graph (DAG) (i.e., break all cycles in the dependency relation among the classes in the SCC).

6.1 Strength of Connection in SCCs Consider the three graphs of Figure 11. Each of the graphs are SCCs of size 5. In order to reduce the clutter in these graphs we have represented two “logical” edges between a pair of nodes with a single edge comprising two arrow heads. When we subsequently talk about removing edges, we are referring to removing logical edges. If the graphs in Figure 11 represent classes and their edges dependencies, then our intuition tells us that (c) is more strongly connected than (b), which in turn is more strongly connected than (a). Our intuition is based on the number of edges we have to remove to turn the SCC into a DAG. In terms of refactoring effort we could argue, all other things being equal, that the amount of work to refactor these classes into a DAG would also produce this ordering — (c), (b), (a) — from most to least work. To capture our intuition we use the concept of a Minimum Edge Feedback Set (MEFS) to classify the connectedness of a SCC. A Minimum Edge Feedback Set (MEFS) is the smallest set of edges that when removed from a (directed) graph turn it into a DAG. For the graph of Figure 11(a) the MEFS is of size 1 because we can make the graph a DAG by removing any one of its edges. For the 12

http://www.netbeans.org/community/releases/40/ whats-new-40.html

Figure 8: SCCs in USES relation over time

Figure 9: SCCs in USES - IN - SIZE relation over time

Figure 10: SCCs in USES - IN - THE - INTERFACE relation over time graph of 11(b) the MEFS is of size 5 (noting a double-headed arrow actually corresponds to two logical edges). For the graph of 11(c) it is less obvious what the MEFS size is (but it is certainly more than 5). This is unsurprising because, as Skiena notes, determining a MEFS for a graph is actually a NP-complete problem [40]. This makes computing the MEFS for the SCCs we have seen so far (the largest of which is around 2100 classes) computationally infeasible. Fortunately Eades et. al. [14] present a good heuristic (referred to herein as Eades’ Heuristic) for computing an edge feedback set that is close to minimal. We refer to this close-to-minimal edge feedback set as mEFS. We do not have the space to restate this heuristic in its entirety but in essence it produces a vertex sequence (v1 , v2 , ..., vn ) that represents a topological ordering. The mEFS is then the set of all of the edges that go from right-to-left in the vertex sequence. While it is tempting to use Eades’ Heuristic verbatim to determine the strength of connectedness of our SCCs there are some domain constraints that we should take into account in computing the mEFS. In particular we cannot easily break the dependency of a derived type on its supertype [24]. Derived types depend on their supertypes by definition, so their corresponding edges ought not appear in the mEFS [24]. More generally the edges that appear in the mEFS may not be the edges we would, at a conceptual level, like to see removed. For instance the mEFS to break the dependency from view on model might be smaller than that to break the dependency from model on view, yet according to Riel [39, p.36] it is the model that should not depend on the view. For our mEFS heuristic we do not deal with conceptually wrong edges although in a recent work we have proposed using a manually-constructed “exclusion set” to do so [35]. In our mEFS heuristic (presented shortly) we do however ensure that subtype to supertype dependencies do not appear in it. So given that we do not take into account conceptually wrong dependencies to include in the mEFS is our heuristic still a reasonable indicator of refactoring burden? On the basis of a metric proposed by Hautus [22] it can be argued that it is a reasonable indicator. Hautus’ PASTA metric identifies which dependencies should be broken in order to transform a cyclic package structure into a layered one. Tacit in Hautus’s metric is that the majority of dependencies reflect the conceptually correct relationship between packages in a cycles, and the minority dependencies should be removed. As far as we can tell Hautus does nothing special with super/subtype dependencies, which may be a drawback of his metric. So we compute the mEFS by first passing an SCC into Eades’

Heuristic — this gives us a vertex sequence. We then perform a stable sort on the vertex sequence so that supertypes appear to the left of their subtypes in the sequence. It should be noted that there is always a topological ordering based on the super-subtype relationship in because the Java Language Specification prohibits cycles in this relation. It should also be noted that Eades’ Heuristic is nondeterministic though we will deal with this as discussed shortly.

6.2 Refactoring Burden Results We ran our mEFS heuristic described above to determine the mEFSs for the largest SCCs in each of the applications in the corpus. Since the heuristic is non-deterministic we ran our it 100 times over the each SCC, each time allowing permutations inherent in the construction of the vertex sequence. We took the best (smallest) mEFS computed by our heuristic for the following graphs. Since our heuristic has fewer choices of mEFS than Eades (recall we ensure no subtype to supertype dependencies appear in it) we also show the best mEFS returned by Eade’s algorithm for the sake of comparison with ours. (In the following charts it appears that the series corresponding to mEFS returned from our algorithm does not appear in any of the bars — this is the best mEFS returned from our algorithm was always almost equal in size to that returned by Eade’s Heuristic. When the two mEFS sizes differed the difference was at most 1). Figure 12 shows the largest SCC in each application from the corpus and the mEFS size required to turn this SCC into a DAG. It shows that not all SCCs, even those of similar sizes, are equally connected is of practical relevance. The x-axis on this plot is the application and the y-axis shows the number of classes in the SCC as well as the number of edges in the mEFS. This axis simultaneously represents both number of classes and number of edges but this is a consequence of stacking the mEFS size bar on top of the SCC size bar for each application. By stacking the bars in this this way (and sorting entries on the x-axis by their biggest SCC size) we can easily visually compare the sizes of the mEFS sets for similar sizes of SCC. Interesting things we can infer from the plot of Figure 12 are: • PMD, JEdit, Netbeans and Jext all have a SCC that is of approximately the same size (about 130 classes) yet the sizes of mEFSs for each of the SCCs varies greatly. Of these applications PMD has the biggest mEFS so we can surmise that refactoring PMD to break up this SCC is probably going to be more work than refactoring say Jext (the application with the smallest mEFS). • Glassfish is particularly interesting because it has the small-

Figure 12: Minimum edge feedback set for largest SCC in USES relation over corpus

Figure 13: Minimum edge feedback set for largest SCC in USES relation over time

est mEFS (4) for the size of its SCC (128). This means that its SCC is relatively weakly-connected compared to the other applications. Figure 13 shows the growth in the largest SCCs in the USES relation for each application over time. It also shows the corresponding mEFS for each SCC. It is interesting because the mEFS size tends to remain constant if the SCC size remains constant between an application’s consecutive versions. We thought that it might be possible for the SCC to become more strongly connected between versions of an application if the classes in it had dependencies added. It seems however that usually the SCC retains the same degree of connectedness if it does not grow in size.

7.

DISCUSSION

In this section we discuss the implications of the results we have presented in the previous two sections.

7.1 No Overall View of a System In Section 5 we saw that for any given application the SCCs USES - IN - THE - INTERFACE relation were typically much smaller than the SCCs in both the USES and USES - IN - SIZE relations. We noted

that this meant that types appearing only in the private parts of a class were the major contributor to large SCCs in the latter dependency relations. So how is it that a type appears only in the private part of a class? One reason a type may appear only in the private part of a class is that a well-designed class hides information (its implementation details) from its clients [6, p.45] [28, p.155]. So at some level we should be pleased to observe this phenomenon. This leads us to the assertion that while individual classes may seem well-designed when they are considered in isolation their interactions with each other, expressed through transitive dependencies, can inhibit a system’s overall structure. Yet, despite this assertion, many OO metrics suites (such as the CK Metrics [10]) do not consider coupling transitively. Our results also confirm Parnas’ early concerns about allowing individual programmers — with no overall view of the system — to make decisions about which other modules to use from the module on which they are working [38]. Parnas surmised this would likely lead to dependency cycles. Almost ironically it seems that successful modularisation has the propensity to breed dependency cycles. Successful modularisation means it should possible to understand a system by considering each of its individual modules in isolation [37]. By considering modules in isolation — which is the way we program, by editing and creating classes, one-by-one, — programmers lose sight of (or never see) the overall structure of the system. In a recent work [35] we have addressed this problem by developing an Eclipse plugin called JooJ that unobtrusively and immediately informs a programmer of statements he or she writes in the context of a single class that induce cycles.

7.2 Big Ball of Mud Foote and Yoder introduce the architectural pattern Big Ball of Mud: a haphazardly structured system whose source code lacks organisation. They claim that, despite all the work done in architectural patterns, Big Ball of Mud is most frequently deployed software architecture. At the level of overall organisation, as opposed to lower level organisation (e.g., control flow, variable naming, long parameter lists etc), we think our results are the first to support this claim. We have seen SCCs in the USES relation involving greater than 1000 classes in two large-scale commercial systems (C1 and D1). We see similar SCCs in the USES relation involving greater

than 500 classes in 5 other open- and closed source systems. We see this, yet it is large-scale software systems where overall organisation is meant to be most important [28]. Even in smaller (around 1000 classes) open source applications, such as Azureus and Hibernate, we see the majority of their classes involved in a single, massive SCC in the USES relation. Foote et. al. also claim that in advanced cases of Big Ball of Mud “. . . there may be no alternative but to tear everything down and start over” [15]. Again we have presented empirical evidence that supports this claim. In our discussions with company ‘B’ we were able to ascertain that two products B5 and B10 had to be thrown away because their source code became too unwieldy. We observed that these products also had a much higher proportion (and larger number) of classes involved in SCCs than the other products from the same company. Finally Foote et. al. claim that without intervention (i.e., continuous refactoring) a design can, and will, degrade over time. This is also a claim of Lehman in his second “law” of software evolution: Increasing Complexity — “As an E-type system evolves its complexity increases unless work is done to maintain or reduce it” [29]. Again we believe our results support these claims. The applications for which we had multiple versions tended to show over time more and more classes becoming involved in cycles in each of the dependency relations. We also saw that dips in the number of classes in SCCs (in ArgoUML, Netbeans, B2 and B5) occurred immediately after significant refactoring activities were performed.

7.3 Absolutes vs. Proportions In Section 5 we discussed results both in terms of absolute values (e.g., the SCC is of size 100) and in terms of proportion of the application (e.g., 90% of this application’s classes are involved in dependency cycles). Which is worse: A SCC that encompasses most of the classes in a small system or a SCC of the same size in the context of a large system? For the purposes of unit testing we are likely to be concerned with absolutes. While unit testing is about testing classes in isolation Binder [5, p.348] and Lakos [28, p.164] note this can be impractical — even in the absence of cycles. Instead Binder and Lakos suggest testing a group of classes through the interface of the one in the group that uses all the others. Binder does not give an upper limit on the number of classes that should be in this group. Lakos implies the group could comprise “one, two or even several classes . . . ” [28, p.474]. In terms of the total cost of the application we might care about the proportion of classes in SCCs. If 90% of the classes in the system are not involved in cycles then we may be satisfied that most of the time the avoid dependency cycles principle is followed by developers. Perhaps the cost of applying the principle to 100% of the classes outweighs its benefit? Another view is that we do not care about classes involved in cycles if they do not exhibit defects and are not frequently modified. Perhaps as Foote et. al. imply the programmers of the system have hidden these cyclically dependent classes away behind a Facade so they no longer have to deal with them — “if you can’t easily make a mess go away, at least cordon it off” [15]. Neither absolutes nor proportions will tell us about this situation however.

7.4 Netbeans vs. Eclipse Perhaps one of the most interesting comparisons between applications in our corpus is that of Netbeans vs. Eclipse. We can reasonably compare these applications with respect to many criteria because both come from the same domain (IDEs), both provide similar functionality and both purportedly have plug-in style archi-

tectures. The Netbeans team13 claim that the IDE is modular in that the core runtime is a generic desktop application that can be used for applications other than IDEs and all of the features of the IDE (e.g. the Java code editor) comprise plug-in modules. The Eclipse team14 similarly claim that “The Eclipse Platform is an IDE for anything, and for nothing in particular”. The Java capabilities of Eclipse are all provided through plugin modules. So what can we infer about the differences between Netbeans and Eclipse from our cycle results? We saw the largest SCC in the USES relation for Netbeans is 135 and Eclipse is 791. If these cycles are confined within individual modules, which they should be because we do not want our modules to be cyclically dependent, then we can infer that Eclipse must have a module comprising at least 791 classes, whereas it is possible that the biggest module in Netbeans comprises only 135 classes. Indeed Netbeans tends to have smaller SCCs in the USES relation than Eclipse, which may suggest according our argument about how cycles affect package structure in Section 2.2 that Netbeans has finer-grained modules than Eclipse.

7.5 Refactoring Techniques In Section 6 we used a mEFS to gauge the effort (in terms of number of dependencies removed) required to break all cycles in a SCC. While the mEFS identifies a “small” set of dependencies we could remove to break all cycles it does not address how we can to remove them. In the past [33] we have used the Dependency Inversion Principle (DIP) [31] to break transitive dependencies. When the DIP is applied to a class, the clients of the class depend only on an interface-type extracted from that class. This means the clients no longer transitively depend on the types used only in the private parts of that class (provided we address the “problem of instantiation” [33] appropriately). It follows that we could potentially break many cycles in the USES and USES - IN - SIZE relations using the DIP. The DIP is not a silver bullet for cycle-breaking though. Sometimes a class should not use another — even in the form of interfacetype as the case when we apply the DIP. For instance if package A depends on package B (i.e., a type declared in A depends on a type declared in B) then there should be no dependencies from types declared in in B to those declared in A. If they did then we would have a cycle among packages which purportedly should be avoided.

7.6 Threats to Validity In Section 4.2.1 we noted that some type information is lost in the conversion from source code to byte code. We noted that this was not problematic for the context of this study because it meant that the dependencies (and thus cycles) we were able to detect from .class files were a (non-strict) subset of those appearing among .java files. We went on to say the results presented in this paper were thus a lower-bound on the actual cycles – but is this really the case? The results presented in this paper are actually a lower bound on the actual cycles among classes that we can determine from static program analysis of compilation dependencies. There could be further “logical” dependencies we were unable to infer because these were expressed through reflection or dynamic class loading. So, in terms of our results, some of the applications we noted as having few cycles (small SCCs) may still be poorly designed with respect to cycles if such compilation dependencies are being avoided with type-unsafe reflection. 13

http://www.netbeans.org/products/platform/ howitworks.html 14 http://www.eclipse.org/whitepapers/ eclipse-overview.pdf

Also in this paper we imply that our results show that cycles are common among classes in Java software in the world today. This assumes that our corpus is representative of real world software. While we tried to ensure representativeness by selecting applications with varying dimensions (size, domain, origin and open or closed-source) our corpus is only a small sample (78 applications) of all the Java software in the world today. Sourceforge alone listed over 16000 projects as being written in Java as at 5 October 2005. It should be noted that deliberately selecting applications for a corpus is not the only way to construct one. In their study of Java bytecode Collberg et. al. [12] were able to collect a much larger sample of applications than us (1132 Java programs) by a more random approach. They collected a “random” sample of Java applications by querying Google using the key-phrase "indexof"jar. Then in the resulting HTML pages Collberg et. al. “searched for any tag whose HREF attribute designated a jar-file”. Using this ‘random’ approach Collberg et. al. were able to collect a larger sample of applications than us but not without cost: as Collberg et. al. state “Because of our random sampling of jar-files from the Internet, the collection is somewhat idiosyncratic. We assume that any two jar-files with the same name are in fact the same program, and keep only one. However, we kept those files whose names indicated that they were different versions of the same program . . . . It is reasonable to assume that such redundancy will have somewhat skewed our results” [12]. We opted not to use the approach of Collberg et. al. because only .jar files that are published inside tags on the web get added to the corpus. It has been our experience that many Java applications are distributed as .exe files or .zip files, which in turn contain .jar or .class files. Furthermore many applications have click-through licenses and popup style downloads which mean Google cannot index them. Finally the approach of Collberg et. al. was ultimately unsuitable for our purposes because we needed to distinguish between classes defined in the source files of the application and those declared in the external libraries.

8. RELATED WORK 8.1 Empirical Studies To the best of our knowledge there has been only three [9, 22, 21] published papers with quantitative empirical evidence of dependency cycles among classes or packages in real Java software applications. Hashim et. al. [21] performed a case study of SCCs in the class dependency graphs of two Java applications — ArchStudio and Ptidej. They found that for ArchStudio, which comprises 2598 classes, there were 1314 classes were involved in SCC >1 in size and the largest SCC involved 742 classes. For Ptidej, which comprises 2235 classes, there were 517 classes were involved in SCC >1 in size and the largest SCC involved 60 classes. The numbers and proportions of classes involved in SCCs in ArchStudio and Ptidej are not dissimilar to that we have observed in some applications in our study. Briand et. al. studied occurrence of dependency cycles among classes in 5 small (ranging from 21-61 classes) Java software systems [9]. Their study looked at elementary circuits (or simple cycles) and not SCCs. Biases in the study were that application ‘frameworks’ were explicitly excluded and only small applications were used — probably deliberate because counting simple cycles is NPcomplete. The different metric used by Briand et. al. makes it difficult to make a comparison with our results. Similarly we cannot easily compare Hautus’s PASTA metric [22] to ours except to say that there cannot be large SCCs in the USES relations of the application’s Hautus found with acyclic package structures if the

classes are distributed somewhat evenly across the packages.

8.2 Language Support There are some programming languages that prohibit cyclic dependencies among its compilation units. Szyperski [42, p.135] identifies Component Pascal as such a language, and implies that this feature exists in order to enforce a program design principle (that of layering). Delphi also disallows a certain form of cyclic dependencies that we find puzzling. The rules on cyclic dependencies in Delphi prevent cycles in the USES - IN - THE - INTERFACE relation [13, p.18-19]. It is hard to see how this relates avoid dependency cycles among modules, because as we previously discussed the major contributor to cycles in Java software are types that are only used in the private part of the class. This feature of Delphi also makes it difficult to see how Lakos’ notion of intrinsic interdependency can be expressed in a type-safe fashion.

8.3 Tool Support There are many tools available for detecting cycles in Java (e.g., ByeCycle15 , JDepend16 , PASTA tool [22], Lattix LDM17 , DependencyFinder18 and Classycle19 ). In a recent work [35] we implied that none of these tools seem capable of computing the USES - IN SIZE and USES - IN - THE - INTERFACE relations. This is problematic because it means they cannot distinguish between “necessary” cycles (those due to intrinsic dependencies) and undesirable cycles. We also speculated in this recent work [35], based on the notion of a poka-yoke device, that the batch-style approach to detecting cycles taken by all these tools (except ByeCycle) is likely to limit their effectiveness. Indeed the prevalence of cycles among Java applications seems to indicate that these tools are not widely (or effectively) used.

9.

CONCLUSIONS AND FUTURE WORK

The results in this paper confirm folklore — that dependency cycles are indeed common place among the classes of OO systems, at least in the context of Java, and that these cycles are big. We have argued that our results support various claims that have otherwise not been supported with empirical evidence, particularly: (1) that the Big Ball Ball of Mud is the most frequently deployed software architecture; (2) that over time systems grow in complexity unless specific steps are taken to refactor them; (3) that individual developers with no overall view of a system should not be able to reference whatever classes they see fit from the source file they are currently editing. The results also highlight a deficiency in current OO metrics suites — that relationships also need to be considered transitively: even though a class may seem well designed when it is considered in isolation its interactions with the rest of the classes in a system can lead to a system with poor overall structure. In terms of future work a wider goal of our research is the development of an even more representative corpus of software comprising thousands of applications on which further empirical studies of this nature can be performed. Another direction is confirming the extent to which cycles effect quality (e.g., through a controlled experiment) so we can determine what proportion or number of classes involved in cycles we can tolerate in an application. Another is determining if the mEFS is really the best way to determine the dependencies that should be removed to break cycles. 15

http://byecycle.sourceforge.net/ http://www.clarkware.com/software/JDepend.html 17 http://www.lattix.com/ 18 http://depfind.sourceforge.net/ 19 http://classycle.sourceforge.net/ 16

At present we are working towards correlating cycles with other source code phenomena — particularly static methods and data — in order to understand the mechanisms that allow them to grow.

10. REFERENCES [1] The American Heritage Dictionary of the English Language, Fourth Edition. Houghton Mifflin Company, 2000. [2] S. Barbey. Test Selection for Specification-Based Testing of Object-Oriented Software Based on Formal Specifications. PhD thesis, no 1753, Ecole Polytechnique F´ed´erale de Lausanne, D´epartement d’Informatique, 1015 Lausanne, Switzerland, 1997. [3] L. Bass, P. Clements, and R. Kazman. Software Architecture in Practice. Addison-Wesley Longman, Reading, MA, 1998. [4] G. Baxter, M. Frean, H. Melton, J. Noble, M. Rickerby, H. Smith, and E. Tempero. Understanding the shape of java software. In Unpublished manuscript submitted to OOPSLA 2006, 2006. [5] R. V. Binder. Testing object-oriented systems: models, patterns, and tools. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 1999. [6] G. Booch. Object oriented design with applications. Benjamin-Cummings Publishing Co., Inc., Redwood City, CA, USA, 1991. [7] G. Booch. Object solutions: managing the object-oriented project. Addison Wesley Longman Publishing Co., Inc., Redwood City, CA, USA, 1995. [8] L. C. Briand, J. W. Daly, and J. K. W¨ust. A unified framework for coupling measurement in object-oriented systems. IEEE Transactions on Software Engineering, 25(1):91–121, January/February 1999. [9] L. C. Briand, Y. Labiche, and Y. Wang. An investigation of graph-based class integration test order strategies. IEEE Trans. Softw. Eng., 29(7):594–607, 2003. [10] S. R. Chidamber and C. F. Kemerer. Towards a metrics suite for object oriented design. In OOPSLA ’91: Conference proceedings on Object-oriented programming systems, languages, and applications, pages 197–211, New York, NY, USA, 1991. ACM Press. [11] P. Coad and E. Yourdon. Object-oriented analysis (2nd ed.). Yourdon Press, Upper Saddle River, NJ, USA, 1991. [12] C. Collberg, G. Myles, and M. Stepp. An empirical study of java bytecode programs. In TR 04-11. Department of Computer Science, University of Arizona, Tucson, AZ 85721, 2004. [13] Delphi 2005 language guide. [14] P. Eades, X. Lin, and W. F. Smyth. A fast and effective heuristic for the feedback arc set problem. Inf. Process. Lett., 47(6):319–323, 1993. [15] B. Foote and J. W. Yoder. Big ball of mud. In N. Harrison, B. Foote, and H. Rohnert, editors, Pattern Languages of Program Design, volume 4, pages 654–692. Addison Wesley, 2000. [16] M. Fowler. Reducing coupling. IEEE Software, 18(4):102–104, 2001. [17] J. Y. Gil and I. Maman. Micro patterns in java code. In Proceedings of the 20th Annual conference on Object-oriented programming, systems, languages, and applications (OOPSLA’05), pages 97 – 116, San Diego, California, October 16-20 2005. [18] J. Gosling, B. Joy, G. Steele, and G. Bracha. Java Language

[19] [20]

[21]

[22]

[23] [24] [25]

[26]

[27]

[28]

[29]

[30]

[31] [32] [33]

[34]

[35]

[36]

Specification, Second Edition: The Java Series. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 2000. J. L. Gross and J. Yellen. Handbook of Graph Theory. CRC Press, 2004. C. Grothoff, J. Palsberg, and J. Vitek. Encapsulating objects with confined types. In OOPSLA ’01: Proceedings of the 16th ACM SIGPLAN conference on Object oriented programming, systems, languages, and applications, pages 241–255, New York, NY, USA, 2001. ACM Press. N. L. Hashim, H. W. Schmidt, and S. Ramakrishnan. Test order for class-based integration testing of java applications. In QSIC, pages 11–18, 2005. E. Hautus. Improving Java software through package structure analysis. In The 6th IASTED International Conference Software Engineering and Applications, 2002. IEEE standard glossary of software engineering terminology. IEEE Std 610.12-1990. S. Jungmayr. Identifying test-critical dependencies. In ICSM, pages 404–413, 2002. D. Kung, J. Gao, P. Hsia, J. Lin, and Y. Toyoshima. Design recovery for software testing of object-oriented programs. In Proc. of the Working Conference on Reverse Engineering, pages 202–211, 1993. D. Kung, J. Gao, P. Hsia, J. Lin, and Y. Toyoshima. Class firewall, test order, and regression testing of object-oriented programs. Journal of Object-Oriented Programming, 8(2):51–65, 1995. D. Kung, J. Gao, P. Hsia, Y. Toyoshima, C. Chen, Y.-S. Kim, and Y.-K. Song. Developing an object-oriented software testing and maintenance environment. Commun. ACM, 38(10):75–87, 1995. J. Lakos. Large-scale C++ software design. Addison Wesley Longman Publishing Co., Inc., Redwood City, CA, USA, 1996. M. M. Lehman, J. F. Ramil, P. D. Wernick, D. E. Perry, and W. M. Turski. Metrics and laws of software evolution - the nineties view. In METRICS ’97: Proceedings of the 4th International Symposium on Software Metrics, page 20, Washington, DC, USA, 1997. IEEE Computer Society. T. Lindholm and F. Yellin. Java Virtual Machine Specification. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 1999. R. C. Martin. The Dependency Inversion Principle. C++ Report, 8(6):61–66, June 1996. R. C. Martin. Granularity [object-oriented design]. C++ Report, 8(10):57–62, Nov.-Dec. 1996. H. Melton and E. Tempero. A simple metric for package design quality. In UoA-SE-2005-7. Department of Computer Science, University of Auckland, 2005. H. Melton and E. Tempero. Identifying refactoring opportunities by identifying dependency cycles. In Proceedings of the Twenty-Ninth Australasian Computer Science Conference, 2006. H. Melton and E. Tempero. JooJ: Real-time support for avoiding cyclic dependencies. In UoA-SE-2005-2. Department of Computer Science, University of Auckland, 2006. B. Meyer. Object success: a manager’s guide to object orientation, its impact on the corporation, and its use for reengineering the software process. Prentice-Hall, Inc.,

Upper Saddle River, NJ, USA, 1995. [37] D. L. Parnas. On the criteria to be used in decomposing systems into modules. Commun. ACM, 15(12):1053–1058, 1972. [38] D. L. Parnas. Designing software for ease of extension and contraction. In ICSE ’78: Proceedings of the 3rd international conference on Software engineering, pages 264–277, Piscataway, NJ, USA, 1978. IEEE Press. [39] A. J. Riel. Object-Oriented Design Heuristics. Addison-Wesley Longman Publishing Co., Inc., Boston, MA, USA, 1996. [40] S. S. Skiena. The algorithm design manual. Springer-Verlag New York, Inc., New York, NY, USA, 1998. [41] W. P. Stevens, G. J. Myers, and L. L. Constantine. Structured design. IBM Syst. J., 13(2):115–139, 1974. [42] C. Szyperski. Component software: beyond object-oriented programming. ACM Press/Addison-Wesley Publishing Co., New York, NY, USA, 1998. [43] N. N. Thuy. Testability and unit tests in large object oriented software. In Conference Proceedings, 5th International Software Quality Week, San Francisco, Calif., May 1992. Software Research Institute. [44] N. N. Thuy. Design for quality in large object oriented software. In Conference Proceedings, 6th International Software Quality Week, San Francisco, Calif., May 1993. Software Research Institute. [45] M. VanHilst and D. Notkin. Using C++ templates to implement role-based designs. In ISOTAS ’96: Proceedings of the Second JSSST International Symposium on Object Technologies for Advanced Software, pages 22–37, London, UK, 1996. Springer-Verlag. [46] M. Winter. Managing object-oriented integration and regression testing. In Presented at 6th EuroSTAR Conference, 1998.

Appendix: Corpus details Application aglets-2.0.2 ant-1.6.5 antlr-2.7.5 aoi-2.2 argouml-0.18.1 axion-1.0-M2 azureus-2.3.0.4 A1-2.0.3 A2-2.6.4 A3-1.1.28 bluej-2.1.0 B1-4.0.x colt-1.2.0 columba-1.0 compiere-251e B2-6.2.x derby-10.1.1.0 drawn-vC drjava-20050814 eclipse-SDK-3.1-win32 B3-2.0.0 fitjava-1.1 fitlibraryforfitnesse-20050923 galleon-1.8.0 ganttproject-1.11.1 geronimo-1.0-M5 glassfish-9.0-b15 hibernate-3.1-rc2 hsqldb-1.8.0.2 C1-5.0.2 ireport-0.5.2 jag-5.0.1 jaga-1.0.b james-2.2.0 jasperreports-1.1.0 javacc-3.2 jboss-4.0.3-SP1 D1-2005 jchempaint-2.0.12 jedit-4.2 jeppers-20050607 jetty-5.1.8 jext-5.0 E1-3 jfreechart-1.0.0-rc1 jgraph-5.7.4.3 jhotdraw-6.0.1 jmeter-2.1.1 joggplayer-1.1.4s jparse-0.96 jre-1.4.2.04 jrefactory-2.9.19 jtopen-4.9 jung-1.7.1 junit-3.8.1 lucene-1.4.3 megamek-2005.10.11 netbeans-4.1 openoffice-2.0.0 B4-2.0.0 pmd-3.3 poi-2.5.1 F1-3.2 B5-3.0-trunk rssowl-1.2 G1-20040331 sablecc-3.1 sandmark-3.4 scala-1.4.0.3 B6-2.5.x sequoiaerp-0.8.2-RC1-all-platforms B7-5.1.x B8-3.3.x B9-5.2 B10-2.0.x tomcat-5.0.28 B11-3.1.x H1-2.3.1-02

#Classes 280 700 209 415 1251 237 1650 155 186 348 396 313 269 1180 1372 2091 1386 17 668 11413 722 37 124 243 310 1719 582 902 217 9240 347 208 100 259 633 125 4143 11644 612 234 20 327 353 192 469 50 300 560 114 69 7257 211 2857 454 48 170 455 8406 2925 902 375 480 2684 1589 189 786 199 837 654 1738 936 1093 178 2241 2273 892 238 1029

Domain Framework for developing mobile agents Java build tool Parser generator 3D modelling and rendering UML drawing/critic SQL database P2P filesharing

IDE High performance collections library Email client ERP and CRM SQL database IDE IDE Automated testing Automated testing TiVo media server Gantt chart drawing J2EE server J2EE server Persistence object mapper SQL database Visual report design for JasperReports J2EE application generator API for genetic algorithms Enterprise mail server Reporting tool Parser generator J2EE server Editor for 2D molecular structures Text editor Spreadsheet editor HTTP Server/servlet container IDE Chart drawing Graph drawing Graph drawing Java performance testing MP3 player Java compiler front-end JRE Refactoring tool for Java Java toolbox for iSeries and AS/400 servers Graph drawing Unit testing framework Text indexing Game IDE Office suite Java code analyser API to access Microsoft format files

RSS Reader Compiler/ Interpreter generating framework Software watermarking/security Multi-paradigm programming language ERP and CRM

Servlet container

Origin Sourceforge Apache antlr.org Sourceforge tigris.org tigris.org Sourceforge A A A bluej.org B hoschek.home.cern.ch Sourceforge Sourceforge B Apache Jakarta University of Auckland Sourceforge www.eclipse.org B fit.c2.com Sourceforge Sourceforge Sourceforge Apache dev.java.net Sourceforge Sourceforge C Sourceforge Sourceforge jaga.org Apache Sourceforge dev.java.net Sourceforge D Sourceforge Sourceforge Sourceforge Sourceforge Sourceforge E Sourceforge Sourceforge Sourceforge Apache joggplayer.webarts.bc.ca www.ittc.ku.edu/JParse Sun Sourceforge Sourceforge Sourceforge Sourceforge Apache Sourceforge netbeans.org openoffice.org B Sourceforge Apache F B Sourceforge G Sourceforge www.cs.arizona.edu/sandmark scala.epfl.ch B Sourceforge B B B B Apache B H

O/C O O O O O O O C C C C C O O O C O C O O C O O O O O O O O C O O O O O O O C O O O O O C O O O O O O O O O O O O O O O C O O C C O C O O O C O C C C C O C C

V N Y N N Y N Y N N N N Y Y N N Y N Y N Y N N N N N N N N N N N N N N N N N N N N N N N N N Y Y Y N N N N N Y N Y N Y N Y N N Y Y N N N N N Y N Y Y N Y N Y N

Notes Donated by IBM

Donated by IBM

Donated by IBM

Donated by IBM

Donated By Sun Donated By Sun