You are on page 1of 66

A Project Report on

SOFTWARE METRIC CALCULATOR


Submitted for partial fulfillment of award of

BACHELOR OF TECHNOLOGY

In
Computer Science & Engineering
By

Shristy Sharma (1235210095)


Swati Nigam (1235210104)
Varsha Mishra (1235210111)

Name of Guide
Mr. Prashant Kumar Mishra

DR. A.P.J. ABDUL KALAM TECHNICAL UNIVERSITY,


LUCKNOW, INDIA
MAY, 2016
CERTIFICATE

This is to Certify that Shristy Sharma, Swati Nigam and Varsha Mishra have
carried out the B. Tech. Final Year Project work presented in this report entitled
“Software Metrics Calculator Tool” for the award of Bachelor of Technology
from Dr. A.P.J. Abdul Kalam Technical University, Lucknow under my
supervision. The project embodies result of original work and studies carried out
by Student herself and the contents of the project do not form the basis for the
award of any other degree to the candidate or to anybody else.

PRASHANT KUMAR MISHRA

Supervisor & Head of Department


CSE Department
Krishna Institute of Technology,
Kanpur

Date:
DECLARATION

This is to certify that report entitled “Software Metrics Calculator Tool ” which
is submitted by Shristy Sharma, Swati Nigam and Varsha Mishra, in partial
fulfillment of the award of Bachelor of Technology in Computer Science to
Department of Computer Science and Engineering, KRISHNA INSTITUTE OF
TECHNOLOGY, KANPUR. It comprises of our original work and due
acknowledgement has been made in the text to all other materials used.

Shristy Sharma (1235210095)

Swati Nigam (1235210104)

Varsha Mishra (1235210111)


ABSTRACT

Measuring software attributes with the purpose of improving software


product quality and project team productivity has become a primary priority for
almost every IT organization that relies on computers. As computers grow more
powerful, the users demand more sophisticated and powerful software. The process
of developing new software and maintaining old systems has been poorly
implemented in many cases, resulting in large cost overruns and squandered
business opportunities.

The application of software metrics has proven to be an effective technique


for improving software quality and productivity. The proposed complexity metric
has enhanced the Halstead complexity metric by giving better results and thereby
improving software quality and productivity.

The purpose of this dissertation is to evaluate existing software complexity


metrics (i.e. cyclomatic complexity metrics and Halstead software science
complexity metric) for sample code sets in ‘C’ language and development of an
improved software complexity metric.

The work involves writing program for determining and analyzing


cyclomatic complexity and Halstead software science complexity metric and
establishing correlation between them. On the basis of results obtained, Halstead
complexity metric has been enhanced to give a better correlation with cyclomatic
complexity metric.
In examining Halstead complexity metric and the cyclomatic number
software complexity measure, one quickly makes the following observations.
There are characteristics of a program which affect its complexity and which
Halstead complexity metric can detect and measure but the cyclomatic number
cannot, and likewise there are characteristics which the cyclomatic number can
detect and measure but Halstead complexity metric cannot. Thus, one would like to
develop a metric which is sensitive to the software characteristics measured by
Halstead complexity metric and which is also sensitive to the characteristics
measured by the cyclomatic number. In this dissertation, concept of an enhanced
Halstead complexity metric is presented in form of Adjoined metric.
ACKNOWLEDGEMENT

We are highly grateful to our guide, Mr. Prashant Kumar Mishra,


Supervisor & Head of Department, Computer Science & Engineering Department,
Krishna Institute of Technology, Kanpur for his constant and invaluable support,
without which this project could not have been a reality. His unending
encouragement and helpful suggestions motivated me to work to the best of our
abilities. Our zeal and enthusiasm was kept alive by his steady faith in our attitude.

Last but not the least we are highly indebted to our teachers, family and
friends whose wishes strengthened us to survive the stormy winds.

Shristy Sharma (1235210095)

Swati Nigam (1235210104)

Varsha Mishra (1235210111)


TABLE OF CONTENTS

TITLE PAGE NO.

ABSTRACT iii
LIST OF TABLES viii
LIST OF FIGURES ix
LIST OF ABBREVIATIONS x

1. INTRODUCTION 1
1.1. OBJECTIVES 1
1.2. MOTIVATION AND THE RELEVANCE OF THE WORK 1
2. LITERATURE SURVEY 3
2.1. SOFTWARE METRICS 3
2.1.1. The scope of Software Metrics 4
2.1.2. Different Types of Software Metrics 4
2.2. PRODUCT METRIC 7
2.2.1. Source Lines Of Code 8
2.2.2. Cyclomatic Complexity 8
2.2.3. Halstead Software Science 9
2.3. CYCLOMATIC COMPLEXITY 9
2.3.1. Advantages of Cyclomatic Complexity 11
2.4. HALSTEAD'S SOFTWARE SCIENCE 12
2.4.1. Basic Entities—Operators and Operands 12
2.4.2. The Theory of Software Science 16
3. METHODOLOGY 20
3.1. INTRODUCTION 20
3.2. ADJOINED METRICS 21
4. IMPLEMENTATION 24
5. RESULTS AND VALIDATIONS 25
6. CONCLUSIONS AND FUTURE WORK 26
7. APPENDICES 27
APPENDIX I: SNAPSHOTS OF THE TOOL 27
APPENDIX II: CODE SNIPPETS 28
8. REFERENCES 76
LIST OF TABLES

Table 2.1: Cyclomatic Complexity Risk Evaluation 12


Table 2.1: List of Operands in C 13
Table 2.2: List of Operators in C 13

LIST OF FIGURES

Figure 2.1: Software Metrics 3


Figure 5.1: Metric results for sample C program source inputs 25
LIST OF ABBREVIATIONS

CC Cyclomatic Complexity
CMMI Capability Maturity Model Integrated
SEI Software Engineering Institute
ISO International Organization for Standardization
SLOC Source Lines of Code
AST Abstract Syntax Trees
CHAPTER 1
INTRODUCTION
1.1 OBJECTIVES

1.1.1 To evaluate existing software complexity metrics (i.e. cyclomatic


complexity metric and Halstead software science complexity metric) for sample
code sets in ‘C’ language.

1.1.2 To develop an improved software complexity metric.

1.2 MOTIVATION AND THE RELEVANCE OF THE WORK

It has been realized for some time that –


1) The software science family of measures and the cyclomatic number are
sensitive to different aspects of software complexity.
2) It would be beneficial if one measure or one family of measures were
simultaneously sensitive to the aspects of the complexity which are measured by
the software science measures and the cyclomatic number.

Hansen proposed an ordered-pair metric which in the first coordinate is a variation


of the cyclomatic number and which in the second coordinate is a software science
measure. However, as Baker and Zweben pointed out, the answers given by
Hansen’s measure are not clear if the two coordinates do not say the same thing.
Baker and Zweben has ended their paper by saying that efforts to find a combining
measure should continue; they were concerned in particular with finding a measure
which would combine the measuring capabilities of the software science effort
measure and the cyclomatic number.
Thus, a metric has been developed, which is sensitive to the software
characteristics measured by both Halstead complexity metric as well as
cyclomatic complexity metric. In this project, an enhanced Halstead complexity
metric is presented in form of Adjoined metric.
CHAPTER 2
LITERATURE SURVEY

2.1 SOFTWARE METRICS

Effective management of the software development process requires


effective measurement of that process. Patrick Henry has said “I know no way of
judging the future but by the past.”

Since quantitative methods have proved so powerful in the other sciences,


computer science practitioners and theoreticians have worked hard to bring similar
approaches to software development.

According to Everald E. Mills, Seattle University “Software metrics deals


with the measurement of the software product and the process by which it is.”

Figure 2.1: Software Metrics


2.1.1 The scope of software metrics

Software metrics is a term applied to a wide variety of measurement


activities, under for a wide variety of reasons. Software metrics provide a
means of measuring software; both under development and after a system
are fielded. Software is an abstract construct, and measuring it is difficult.
What, exactly, do you measure to get meaningful results?

1. Cost and effort estimation- Various models work to predict the cost
and time to complete a Project
2. Productivity models and measures
3. Data collection- Consistent, meaningful data collection is difficult, and
made more difficult by try to collect data across diverse projects.
4. Quality models and measures- Bad code is not worth much, even if lots
of it is written quickly.
5. Reliability modeling- Just how reliable is the software? When can we
expect the next failure?
6. Performance evaluation- How well does the system perform? Response
and completion time, transactions processed, etc.

2.1.2 Different Types of Software Metrics

Software Metrics can be broadly classified into 3 types.


1. Process Metrics
2. Product Metrics
3. Project Metrics
1. Process Metric:
They are used to assess people’s productivity (called private metrics),
productivity of the entire organization (called public metrics), and software
process improvement. Process assessment is achieved by measuring specific
attributes of the process, developing a set of metrics based on the identified
attributes, and finally using the metrics to provide indicators that lead to the
development of process improvement strategies. Private metrics are
designed to help individual team members in self-assessment allowing an
individual to track work tasks and evaluate self-productivity. Pubic metrics
allows teams to track their work and evaluate performance and productivity
of the process. A good example is team’s effectiveness in eliminating
defects through development, detecting defects through testing, and
improving response time for fixes.

2. Product Metric:
These metrics focus on measuring key characteristics of the software
product. There are many product metrics applicable to analysis, design,
coding, and testing. Commonly used product metrics include:

a) Specification quality metrics: These metrics provide indication of


the level of specificity and completeness of requirements.
b) System size metrics: They measure the system size based on
information available during the requirements analysis phase.
c) Architectural metrics: These metrics provide an assessment of
the quality of the architectural design of the system.
d) Length metrics: They measure the system size based on lines of
code during implementation phase.
e) Complexity metrics: measure complexity of developed source code.
f) Testing effectiveness metrics: They measure the effectiveness of
conducted tests and test cases. Other product metrics focus on design
features, quality attributes, code complexity, maintainability,
performance characteristics, code testability, and others.

3. Project Metric:
Project metrics are tactical and related to project characteristics and
execution. They often contribute to the development of process metrics. The
indicators derived from project metrics are utilized by project managers and
software developers to adjust project workflow and technical activities. The
first application of process metrics often occurs during cost and effort
estimation activity. Metrics collected from past projects are used as basis
from which effort and time estimates are made for new projects. During the
project, measured efforts and expended time are compared to original
estimates to help track how accurate the project estimates were. When the
technical work starts, other project metrics begin to have significance for
different measures, such as production rates in terms of models created,
review hours, function points, and delivered source code lines.
Some common examples of software metrics are:-
a) Source lines of code.
b) Cyclomatic Complexity is used to measure code complexity.
c) Function point analysis (FPA), is used to measure the size
(functions) of software.
d) Bugs per lines of code.
e) Halstead software science metric
f) Code coverage, measures the code lines that are executed for a
given set of software tests.
g) Cohesion, measures how well the source code in a given module
work together to provide a single function.
h) Coupling, measures how well two software components are data
related, i.e. how independent they are.

The above list is only a small set of software metrics, the important
points to note are:-
a) They are all measurable, that is they can be quantified.
b) They are all related to one or more software quality characteristics.

2.2 PRODUCT METRIC

Product metrics are metrics that can be calculated from the document
independent of how it was produced. Generally, these are concerned with the
structure of the source code. Product metrics could be defined for other documents.
For example, the number of paragraphs in a requirements specification would be a
product metric.

Product metrics describe the characteristics of the product such as size,


complexity, design features, performance, and quality level.

2.2.1 Source Lines of Code

Source Lines of Code (SLOC) is perhaps the oldest of software metrics and
still a benchmark for evaluating new ones. There are many different ways to count
lines of source code. The definition may be a simple as the number of NEW LINE
characters in the file. Often comments are excluded from the count of lines.
Sometimes blank lines or lines with only delimiters are excluded.
2.2.2. Cyclomatic Complexity

One of the most popular metrics used in software development is cyclomatic


complexity, developed by McCabe. His intention was to find a methodology that
would allow the modularization of a software system so that the resulting modules
would be testable and maintainable. In order to do this, he first had to devise a
measure that would predict the maintainability and testability of a program called
cyclomatic complexity. Cyclomatic complexity may be considered a broad
measure of soundness and confidence for a program. It measures the number of
linearly-independent paths through a program module. This measure provides a
single ordinal number that can be compared to the complexity of other programs.
Cyclomatic complexity is often referred to simply as program complexity, or as
McCabe's complexity

2.2.3. Halstead Software Metrics

The theory of software science as presented by Halstead in his 1977


monograph “Elements of Software Science” , a computer program is considered in
software science to be a series of tokens which can be classified as either
"operators" or "operands."' All software science measures are functions of the
counts of these tokens.
Halstead uses these primitive measures to develop expressions for the
overall program length, potential minimum volume for an algorithm, the actual
volume (number of bits required to specify a program), the program level (a
measure of software complexity), the language level (a constant for a given
language), and other features such as development effort, development time, and
even the projected number of faults in the software.
2.3. CYCLOMATIC COMPLEXITY

McCabe’s cyclomatic number, introduced in 1976, is, after lines of code,


one of the most commonly used metrics in software development. Cyclomatic
complexity is the most widely used member of a class of static software metrics.
Cyclomatic complexity may be considered a broad measure of soundness and
confidence for a program.
McCabe tries to measure the complexity of a program. The word
"cyclomatic" comes from the number of fundamental cycles in connected,
undirected graphs. More importantly, it also gives the number of independent paths
through strongly connected directed graphs. A strongly connected graph is one in
which each node can be reached from any other node by following directed edges
in the graph. Graph theory uses a formula, CC = e - n + 2p to calculate the
cyclomatic number. McCabe uses the slightly modified formula:

CC = e - n + 2p
Where,
e = Number of edges
n= Number of nodes
p = Number of strongly connected components (which is normally 1)
Cyclomatic complexity measures the amount of decision logic in a single
software module. It gives the number of recommended test for software.
Cyclomatic complexity CC, for a flow graph, G, is also defined as
CC = P + 1
Where, P is number of predicate nodes contained in the flow graph G.
Example:
C code module with complexity six.
complexity6( int i, int j ) {
if ( i>0 && j>0 )
{
while ( i>j ) {
if ( i% 2 && j % 2)
print( "%d\ n", i );
else
print ("%d\ n", j );
i--;
} } }

Starting with 1, each of the two "if" statements add 1, the "while" statement adds 1
and each of the two "&&" operators adds 1, for total of six.

Software Technology Roadmap, Carnegie Mellon University [9], has


categorized different programs according to their Cyclomatic Number.

Cyclomatic Complexity Risk Evaluation


1-10 a simple program, without much risk
11-20 more complex, moderate risk
21-50 complex, high risk program
greater than 50 untestable program (very high risk)

Table 2.1: Cyclomatic Complexity Risk Evaluation


2.3.1 Advantages of Cyclomatic Complexity:

1. It can be used as a ease of maintenance metric.


2. Used as a quality metric, gives relative complexity of various designs.
3. It can be computed early in life cycle than of Halstead's metrics.
4. Measures the minimum effort and best areas of concentration for
testing.
5. It guides the testing process by limiting the program logic during
development.

2.4 HALSTEAD SOFTWARE SCIENCE

Maurice Halstead was one of the first researchers in software metrics. He did
his work in the late 1960s and 1970s. His goal was to identify what contributed to
the complexity in software. He empirically looked for measures of intrinsic size.
After finding what he felt were good measures and prediction formulas, he tried to
develop a coherent theory.

2.4.1 Basic Entities—Operators and Operands

The basic approach that gave Halstead good results was to consider any
program to be a collection of tokens, which he classified as either operators or
operands. Operands were tokens that had a value. Typically, variables and
constants were operands. Everything else was considered an operator. Thus,
commas, parentheses, arithmetic operators, brackets, and so forth were all
considered operators. All tokens that always appear as a pair, triple, and so on are
counted together as one token. For example, a left parenthesis and a right
parenthesis has been considered as one occurrence of the token parenthesis. A
language that has an if-then construction is considered to have an if-then token.
No standard has been accepted for deciding ambiguous situations. The good
news is that as long as an organization is consistent, it doesn’t matter.
The author recommends a syntax-based approach where all operands are
user defined tokens and all operators are the tokens defined by the syntax of the
language.
IDENTIFIER All identifiers that are not reserved words
TYPENAME (type specifiers) Reserved words that specify type: int, float,
char, double, long, short, signed, unsigned, void. This class
also includes some compiler specific nonstandard keywords.
CONSTANT Character, numeric or string constants.

Table 2.2: List of Operands in ‘C’

SCSPEC (storage class specifiers) Reserved words that specify storage


class: auto, extern, register, static, typedef..
TYPE_QUAL (type qualifiers) Reserved words that qualify type: const, final,
volatile.
RESERVED Other reserved words of C: break, case, continue, default, do, if,
else, enum, for, goto, if, new, return, sizeof, struct, switch, union,
while. This class also includes some compiler specific
nonstandard keywords.
OPERATOR ! != % %= & && || &= ( ) { } [ ]
* *= + ++ += , - -- -= -
> . ... / /= : :: < << <<= <= = == > >= >> >>=
? ^ ^= | |= ~ ; =& “ “ ‘ ‘ # ##

Table 2.3: List of Operators in ’C’


Some special cases are as follows in C :
 A pair of parenthesis is considered a single operator.
 The delimiter ; is considered a single operator.
The ternary operator ‘?’ followed by ‘:’ is considered a single operator
as it is equivalent to “if-else” construct. x = (a == 3) ? a : b; is
equivalent to:
if (a == 3)
x = a;
else
x = b; .
 A label is considered an operator if it is used as the target of a GOTO
statement.
 Local variables with the same name in different functions are counted as
unique operands.
 The following control structures case ...: for (...), if (...), switch
(...), while(...) are treated in a special way. .
The colon and the parentheses are considered to be a part of the constructs.
The case and the colon or the “for (...)”, “if (...)”, “switch (...)”, “while(...)”
are counted together as one operator.
 In the array variables such as “array-name [index]” “arrayname” and
“index” are considered as operands and [ ] is considered as operator.
 In the structure variables such as “struct-name, member-name” or “struct-
name -> member-name”, struct-name, member-name are taken as operands
and ‘.’, ‘->’ are taken as operators. Some names of member elements in
different structure variables are counted as unique operands. The comments
are considered neither an operator nor an operand.
 The function name is considered a single operator when it appears as calling
a function; but when it appears in declarations or in function definitions it is
not counted as operator.
 Same is the case for the identifiers (or variables) and constants; when they
appear in declaration they are not considered as operands, they are
considered operands only when they appear with operators in expressions.
As an example, func(a,b);-----here func, a and b are considered operands
and ‘,’ and ‘;’ operators as it is calling a function, but for the following case
we do not treat func, a and b as operands
int func(int a , int b) {
…….
…….
}

2.4.2 The Theory of Software Science

Halstead's theory of software science is one of "the best known and most
thoroughly studied composite measures of (software) complexity". Software
science proposed the first analytical "laws" for computer software.

A computer program is considered in software science to be a series of


tokens which can be classified as either "operators" or "operands."' All software
science measures are functions of the counts of these tokens.
The basic metrics are defined as –
n1 = the number of distinct operators that appear in a program.
n2 = the number of distinct operands that appear in a program.
N1 = the total number of operator occurrences.
N2 = the total number of operand occurrences.
Halstead uses these primitive measures to develop expressions for the overall
program length, potential minimum volume for an algorithm, the actual volume
(number of bits required to specify a program), the program level (a measure of
software complexity), the language level (a constant for a given language), and
other features such as development effort, development time, and even the
projected number of faults in the software.

a) Vocabulary of a program ( n )
The size of the vocabulary of a program, which consists of the number of
unique tokens used to build a program is defined as:
n = n1 + n2
Here -
n = vocabulary of a program
n1 = number of unique operators
n2 = number of unique operands

b) Potential Operands, ( n2 *)
Halstead wanted to consider and compare different implementations
of algorithms. He developed the concept of potential operands that
represents the minimal set of values needed for any implementation of the
given algorithm. This is usually calculated by counting all the values that are
not initially set within the algorithm. It includes values read in, parameters
passed in, and global values accessed within the algorithm.
c) Length of a program ( N )
The length of the program in the terms of the total number of
tokens used is:
N = N1 + N2
Here -
N = vocabulary of a program
N1 = number of unique operators
N2 = number of unique operands

It should be noted [4] that N is closely related to the traditional "lines


of code" (LOC) measure of program length.
N = 2 * LOC

d) Estimated Program Length ( Ne )


The estimate of length is the most basic of Halstead’s prediction
formulas. Based on just an estimate of the number of operators and
operands that has been used in a program, this formula allows an
estimate of the actual size of the program in terms of tokens:

Ne = n1log2n1 + n2 log2n2
e) Volume (V )
Halstead thought of volume as a 3D measure, when it is really related
to the number of bits it would take to encode the program being measured.
The unit of measurement of volume is the common unit for
size “bits”. It is the actual size of a program if a uniform binary encoding
for the vocabulary is used.

V = N * log2n
f) Potential Volume (V* )
The potential volume is the minimal size of a solution to the problem,
solved in any language. Halstead assumes that in the minimal
implementation, there would only be two operators: the name of the
function and a grouping operator. The minimal number of operands is n2*.

V* = (2+n2*) log2 (2+n2*)

g) Program Level (L )
Since we have the actual volume and the minimal volume, it is natural
to take a ratio. Halstead divides the potential volume by the actual. This
relates to how close the current implementation is to the minimal
implementation as measured by the potential volume. The implementation
level is unit less.
L = V* / V
The value of L ranges between zero and one, with L=1 representing
a program.

h) Program Difficulty (D )
As the volume of an implementation of a program increases, the
program level decreases and the difficulty increases.
D=1/L
i) Effort (E )
Since Halstead wanted to estimate how much time (effort) was
needed to implement this algorithm. He used a notion of elementary
mental discriminations (emd).
E = V/L = D*V
The units are elementary mental discriminations (emd). Halstead’s
effort is not monotonic—in other words, there are programs such that if you
add statements, the calculated effort decreases.
CHAPTER 3

METHODOLOGY

3.1 INTRODUCTION
This project work has been done in two phases -

1. Evaluation of existing software complexity metrics (i.e. cyclomatic


complexity metrics and Halstead software science complexity metric) for
sample code sets in ‘C’ language has been done. The work involves writing
a program for determining and analyzing cyclomatic complexity and
Halstead software science complexity in Java and establishing correlation
between them.
2. On the basis of results obtained, Halstead complexity metric has been
enhanced to give a better correlation with cyclomatic complexity metric.

Below is the further break up of tasks which have been achieved –

1. A scanner has grouped input characters into tokens.


2. A parser has recognized sequences of tokens according to grammar and
generated Abstract Syntax Trees.
3. Semantic analysis has been done with some limitations. Full semantic
analysis was not done and does not include any type of syntax checking.
This has been done to identify various semantic components associated
with control structures present and their relation with other structures in
the input code.
4. After we have determined the structures in input code through above
phases, we have calculated their complexity based on the formula
suggested by both techniques.
5. After determining the complexity data for sample set, a comparison table
containing program name, cyclomatic complexity (CC) and Halstead
Software Science measures such as Program length, Vocabulary size, Effort
to implement has been prepared according to different sample program
pairs.
6. Halstead software science metric has been enhanced so that it presents a
better correlation with the cyclomatic complexity metric. New metric
(Adjoined Metric) has combination of the measuring capabilities of the
Halstead software science effort measure and the cyclomatic number.

3.2. ADJOINED METRIC (ENHANCED HALSTEAD COMPLEXITY METRIC)

Adjoined Metric is built from a software science measure by allowing


certain operators and operands to contribute extra values, called adjoin, to NI and
N2, respectively.

The objective is to assign adjoin so that the length, volume, and effort
measures can detect complexity produced by non-sequential control structures.
In our development of these adjoined measures, we have limited ourselves to
programs which have the following properties:
1) each module has a single entry and a single exit;
2) each program control graph (with the extra added arc) is strongly
connected;
3) the only control structures used are SEQUENCE, IF THEN ELSE, DO WHILE,
and DO UNTIL;

For the length, volume, and effort measures, corresponding Adjoined


measures are defined as follows:
When determining the value of a Halstead software science measure, each
occurrence of an operator or an operand adds one to the corresponding N1 or N2,
count. When determining the value of an adjoined measure, each occurrence of
an operator or an operand adds one to the corresponding N1 or N2 count, and in
addition each operator or operand that is part of a control structure (defined
below) also adds a value equal to the nesting level of its control structure to the
corresponding N1 or N2. Thus, each operator or operand that is part of a control
structure contributes a value equal to the cyclomatic complexity of its control
structure to N1 or N2. By an operator or an operand being “part of a control
structure” we mean that the operator or operand is the control structure itself,
which is an operator, or it is in the test part of the control structure, e.g., it is an
operator or operand in the IF part of an IF THEN ELSE control structure. To
determine the value of Adjoined measure for a program with more than one
module, the Adjoined measure is applied to each module separately, and then the
separate values are summed to get the measures value for the program.

To help us present clearly the formulas for our Adjoined measure, we have
introduced some notation. Given a module, we define T to be the set of all
operators in the module.
Each operator will need to be in T as often as it is in the module. Since sets
by definition do not allow repetition, alteration is needed in operators before
they can be put into T.

R is defined to be the set of all operands in the module; each operand is in


R as often as it is in the module.

Using a lower case ‘a’ to stand for an adjoined measure, we have the
following formulas:

Adjoined total of all operators Na1 = ∑x є T [ 1 + δ(x) * L(x) ]


Adjoined total of all operands Na2 = ∑x є R [ 1 + δ(x) * L(x) ]
Adjoined Length Na = Na1 + Na2
Adjoined Volume Va = Na * log (n1 + n2)
Adjoined effort Ea = (Va)2/V*

As before, the presence of V* makes the use of an approximation desirable.


Here approximation of the adjoined effort is -
Ea = Va * [ (n1 * Na2) / (2*n2) ]

Each occurrence of an operator or an operand contributes at least one to


Na1, or Na2, and each operator and operand that is part of control structure does
indeed contribute a value equal to the cyclomatic complexity of its control
structure to Na1, or Na2.

In general, the values of the adjoined measures equal the values of the
corresponding unadjoined measures if and only if the cyclomatic number of each
program module is 1.
CHAPTER 4

IMPLEMENTATION

A tool for automatically collecting metrics data from sample C program


codes and calculation of cyclomatic complexity metric, Halstead software science
complexity metric and Adjoined metric has been developed as a part of this
dissertation work. The tool is developed using Object Oriented programming
language Java (JDK 1.6) on Windows environment.

This tool calculates Cyclomatic number (CC), Number of unique operators


(n1), Number of unique operands (n2), Total number of operators (N1), Total
number of operands (N2), Program length (N), Vocabulary size (n), Program
volume (V) , Difficulty level (D), Halstead effort (E), Adjoined program length (N’a),
Adjoined program volume (Va), Adjoined difficulty level (Da), Adjoined effort (Ea),
Constant level adjoined length (N’a) and Constant level adjoined effort (E’a) from
the input sample C program.

Ten pairs of sample C programs have been taken (Some of the programs
are program segments and not complete programs). In each pair, cyclomatic
complexity of the second program is lesser than that of first program.

For each pair of sample C programs, the values of following eight measures
are evaluated -
a) Halstead program length (N)
b) Halstead vocabulary size (n)
c) Halstead effort (E)
d) Cyclomatic number (CC)
e) Adjoined length (Na)
f) Adjoined effort (Ea)
g) Constant level adjoined length (N’a)
h) Constant level adjoined effort (E’a).

Sensitivity of adjoined effort is checked against the increasing cyclomatic


complexity and Halstead effort in the sample program pair.

Also, to check the effect of levels in control structure on the adjoined


metric, constant level adjoined effort has been tested against two sample C
programs having same number of control statements and number of operands &
operators in the control statements. Difference of adjoined effort and constant
level adjoined effort should be approximately same.

It can be easily seen that for each pair of sample programs, Halstead
software science metric is unable to detect corresponding difference in
cyclomatic complexity, but our adjoined metric has detected the difference.
CHAPTER 5
RESULTS AND VALIDATIONS

From the experimentation carried out on a set of 10 sample C program


pairs, it can be clearly observed that adjoined metric is sensitive to the software
characteristics measured by the cyclomatic complexity.

S. Program CC N n E Na Ea
No

1A. 1.C 4 28 16 1344.0 64 6144.000

1B. 2.C 2 47 20 2843.8286 50 4538.0244

2A. INSERT.C 4 88 22 18836.63915 155 60826.64727

2B. REVERSE1.C 2 48 20 2904.33567 50 4538.02449

3A. SUM.C 2 39 16 2808.0 42 4032.0

3B. SUM1.C 1 32 15 1250.20499 42 2461.34107

4A. FACTORIAL1.C 2 74 24 7125.03172 74 7125.03172

4B. FIBONACCI.C 3 52 21 4796.41062 68 8362.97237


5A. PALINDROME.C 3 48 21 5059.94967 59 8292.69529

5B. LEAPYEAR.C 4 30 11 4670.23268 54 14010.69805

6A. ARMSTRONG.C 5 76 25 8470.39369 97 18018.16201

6B. ARMSTRONG1.C 7 120 34 18314.86622 166 33780.75326

7A. ARRAY.C 4 67 21 8239.98748 115 24750.70867

7B. ARRAY1.C 3 67 21 8239.98748 89 16418.48252

8A. DECIMAL 3 37 18 3240.03172 52 6017.41080


NUMBER.C
8B. DMAS.C 1 63 21 4980.88795 63 4980.88795

9A. SWAPPING.C 1 38 12 1634.74290 38 1634.74290

9B. REVERSE.C 2 36 17 4120.16254 39 4463.50942

10A. SUM2.C 1 44 15 2578.54779 44 2578.54779

10B. GCD.C 2 63 17 3862.65238 66 5395.45095


11A. PRIME 6 65 22 9275.61776 167 65535.80706
NUMBER.C
11B. PASCAL 4 83 22 14805.31297 143 45914.30794
TRIANGLE.C

12A. ARRAY2,C 5 125 43 12209.09569 239 46687.58194

12B. PYRAMID.C 4 61 22 6528.60788 133 37958.68193

13A. VOWEL.C 11 44 12 5520.84225 74 15917.23350

13B. SLEEPING.C 15 144 43 21878.69949 288 109393.49745

14A. SELECTIO.C 6 110 19 19625.42511 236 90225.98038

14B. MERGESHO.C 2 45 21 2767.15997 54 4980.88795

15A. MERGE1.C 6 178 34 28978.18834 288 70329.08631

15B. ABC(2).C 4 78 20 18878.18191 142 63826.23410

16A. ABC3.C 3 107 29 12475.29519 119 18499.19162

16B. INSERTIO.C 6 105 24 34662.31650 202 133367.38922


17A. ABC4.C 3 65 21 9136.02020 95 23367.12869

17B. DECREASI.C 6 105 24 34662.31650 202 133367.38922

18A. SUM OF 2 38 16 3648 47 5640


DIGITS1.C
18B. PASSWORD.C 3 38 17 2795.82458 47 3457.99356

19A. AREA OF 1 50 18 5003.91000 50 5003.91000


TRIANGLE.C
19B. NUMBER.C 2 19 12 681.14287 22 1183.03762

20A. BUBBLE.C 6 102 22 25472.27340 237 143736.39993

20B. ROWS.C 7 106 22 18907.99006 326 220973.75556

Figure 5.1: Metric results for sample C program source inputs


CHAPTER 6

CONCLUSIONS AND FUTURE WORK

Now we have a solution to the problem which was addressed by Hansen,


Baker and Zweben, and others. The problem was to define a software measure or
a family of measures which simultaneously detect those aspects of software
complexity which are detected by the cyclomatic number and also by Halstead
software science measures. Our family of Adjoined measures is built on the
Halstead software science measures by adding adjoin to certain operators and
operands; the size of adjoin is determined by a theorem which relates nesting
levels and the cyclomatic number (presence of control statements in the
program). Thus, the adjoined metric enhances the sensitivity of the Halstead
software science to the cyclomatic complexity.

Future work should include enhancing our adjoined metric concept for
some existing limitations of our dissertation work. Adjoined metric should be
enhanced to determine how adjoins can be added for CASE statements, GOTO
statements and multiple-test predicates, and testing the adjoined measures on
larger programs.
APPENDICES

APPENDIX 1: SNAPSHOTS OF THE TOOL


APPENDIX 2: CODE SNIPPET
// Main.JAVA

/* * this is the main class


* java Main "<path to C code file>"
* example: java Main "e:\1.c"
*/

import java.util.*;

public class Main {

public static void main(String args[]) {


try
{
// check if the arguments are supplied
if(args.length > 0)
{
// Read the first argument
String filePath = args[0];
// Print it on screen
System.out.println("Path to input C file : " + filePath);
// Instantiate Lexical analyzer
Lex lx = new Lex(filePath);
// Start analysis to build the Symbol table
lx.StartAnalysing();
// print the symbol table
lx.PrintSymbolTable();
// Read the SymbolTable from Lexical Analyzer
ArrayList<SymbolInfo> symTable = lx.GetSymbolTable();
/* Halstead */
// First we do Halstead
CalculateAndPrintComplexity(new Halstead(symTable));
/* Cyclomatic */
// Now we do Cyclomatic
CalculateAndPrintComplexity(new
Cyclomatic(symTable));
/* Adjoined Halstead */
// Now we do Adjoined Halstead
CalculateAndPrintComplexity(new
AdjoinedHalstead(symTable));

}
else
{
// if there was no arguments .. print error message
System.out.println();
System.out.println("ERROR: Argument missing.");
System.out.println("Program requires path to input C file
as an argument.");
System.out.println("example : java PRKM
\"d:\\sample.c\"");
}
}
catch(Exception e)
{
// In case there is any exception print it
System.out.println(e.toString());
}
}

public static void CalculateAndPrintComplexity(IComplexity complexity)


{
if(complexity!=null)
{
// Now calculate
complexity.Calculate();
// Now Print result for complexity
complexity.PrintResults();
}
}
}

/* ParsingSymbol.java
* these are the various symbol which can be present in C code file
*/
public enum ParsingSymbol {
NONE,
MAIN,
LP,
RP,
BEGIN,
END,
SEMICOLON,
COLON,
PLUS,
MUL,
MINUS,
INCREMENT,
DECREMENT,
INT,
WHILE,
FOR,
IF,
IDENTIFIER,
COMMA,
CONDITIONALOP,
RELOP,
LOP,
BREAK,
ELSE,
EQUAL,
DIV,
CHAR,
VOID,
FLOAT,
DOUBLE,
SWITCH,
CASE,
MODIFIER,
ARLP,
ARRP,
EXTERN,
QUALIFIER,
ENUM,
CONTINUE,
DEFAULT,
LITERAL,
STRUCT,
RETURN,
REMAINDER
};

/* SymbolInfo.java
* These are like columns in Symbol table or say a row in symbol table
* Lexeme, token, SymbolType, Symbol
*/
public class SymbolInfo {
public String lexeme;
public String token;
public SymbolType symType;
public ParsingSymbol symbol;
}

/* SymbolType.java
* This is an enum to categorize the symbol.
* basically to explain what type of symbol this is
*/
public enum SymbolType {
NONE,
CONDITION,
ASSIGNMENT,
OPERATOR,
SCOPE,
ENDOFSTATEMENT,
SEPARATOR,
MAINMETHOD,
FLOW,
DATATYPE,
QUANTIFIER,
LOOP,
ARRAYRP,
ARRAYLP,
IDENTIFIER,
LITERAL,
METHODRP,
METHODLP,
RETURN
}
// Cyclomatic.java
import java.util.*;
/*
* this is the class to calculate cyclomatic complexity
*/
public class Cyclomatic implements IComplexity{
// contain decision symbols for result printout
private Map<String, Integer> decisionSymbols = new HashMap<String,
Integer>();
// number of decision
long numberofDecisions;
// symbol table
private ArrayList<SymbolInfo> symTable;
// constructor
public Cyclomatic(ArrayList<SymbolInfo> symbolTable)
{
this.symTable = symbolTable;
this.numberofDecisions = 0;
this.decisionSymbols = new HashMap<String, Integer>();
}
// this method does the calculation
public void Calculate() {
int count = symTable.size();
SymbolInfo symInfo;
SymbolInfo previousSymInfo = null;
// read symbols one by one from Symbol table
for(int i=0;i<count;i++)
{
symInfo = (SymbolInfo)symTable.get(i);
// if Symbol type of symbol satisfies following then there is a
decision
if(symInfo.symType == SymbolType.LOOP || (symInfo.symType
== SymbolType.FLOW && symInfo.symbol == ParsingSymbol.CASE &&
previousSymInfo != null &&
previousSymInfo.symbol != ParsingSymbol.COLON ) ||
(symInfo.symType == SymbolType.FLOW &&
symInfo.symbol != ParsingSymbol.CASE && symInfo.symbol !=
ParsingSymbol.ELSE
&& symInfo.symbol != ParsingSymbol.DEFAULT
&& symInfo.symbol != ParsingSymbol.BREAK &&
symInfo.symbol != ParsingSymbol.CONTINUE &&
symInfo.symbol != ParsingSymbol.SWITCH) ||
(symInfo.symType == SymbolType.CONDITION &&
symInfo.symbol == ParsingSymbol.CONDITIONALOP))
{
if(this.decisionSymbols.containsKey(symInfo.lexeme))
{
this.decisionSymbols.put(symInfo.lexeme,
this.decisionSymbols.get(symInfo.lexeme) + 1);
}
else
{
this.decisionSymbols.put(symInfo.lexeme, 1);
}
this.numberofDecisions++;
}
previousSymInfo = symInfo;
}
}

// this prints the results


public void PrintResults() {
System.out.println("=================== CC Cyclomatic Result
=========================");
System.out.println("CC Cyclomatic complexity (Number of decisions +
1) : " + (this.numberofDecisions + 1));
System.out.println();
System.out.println("------------------ Decision count per symbol ----------
----------");
System.out.println(this.decisionSymbols);
System.out.println();

System.out.println("=============================================
=====================");
}
}

// Halstead.java
import java.util.*;
/*
* this is the class to calculate halstead complexity.
*/
public class Halstead implements IComplexity{
// distinct operand
private ArrayList<String> distOperand;
// distinct operator
private ArrayList<String> distOperator;

// n1 , n2, N1 and N2 as per definition of Halstead


private long n1 = 0, n2 = 0, N1 = 0, N2 = 0;
// symbol table
private ArrayList<SymbolInfo> symTable;
// distinct operand
private ArrayList<String> distinctOperandList;
// distinct operator
private ArrayList<String> distinctOperatorList;
// constructor
public Halstead(ArrayList<SymbolInfo> symbolTable)
{
this.symTable = symbolTable;
this.distinctOperatorList = new ArrayList<String>();
this.distinctOperandList = new ArrayList<String>();
this.distOperand = new ArrayList<String>();
this.distOperator = new ArrayList<String>();
}

// function to determine if this is an operand as per definition of Halstead


private boolean IsOperand(SymbolType sType)
{
return (sType == SymbolType.IDENTIFIER || sType ==
SymbolType.MAINMETHOD);
}
// function to determine if this is an operator as per definition of Halstead
private boolean IsOperator(SymbolType sType)
{
return (sType == SymbolType.SEPARATOR || sType ==
SymbolType.ENDOFSTATEMENT || sType == SymbolType.QUANTIFIER || sType ==
SymbolType.ASSIGNMENT || sType == SymbolType.CONDITION || sType ==
SymbolType.ARRAYLP
|| sType == SymbolType.FLOW || sType == SymbolType.LOOP
|| sType == SymbolType.OPERATOR || sType == SymbolType.RETURN );
}
// this method does the calculation
public void Calculate()
{
int count = symTable.size();
SymbolInfo symInfo;
SymbolInfo previousSymInfo = null;
// read symbol one by one from Symbol table
int scopeCounter = 0;
int funcCount = 0;
boolean isFloworLoop = false;
for(int i=0;i<count;i++)
{
symInfo = (SymbolInfo)symTable.get(i);
if(symInfo.symType == SymbolType.MAINMETHOD)
{
AddtoOperator(symInfo.lexeme);
}
if(symInfo.symType == SymbolType.SCOPE)
{
if(symInfo.symbol == ParsingSymbol.BEGIN)
{
AddtoOperator(symInfo.lexeme);

scopeCounter++;

if(!isFloworLoop && previousSymInfo!=null &&


previousSymInfo.symType == SymbolType.METHODRP)
{
funcCount++;
distinctOperandList.clear();
//distinctOperatorList.clear();
}
}
else if(symInfo.symbol == ParsingSymbol.END)
{
scopeCounter--;
if(scopeCounter == 0)
{
isFloworLoop = false;
}
}
continue;
}
// if code block is inside a valid scope
if(scopeCounter>0 && funcCount>0)
{
if(symInfo.symType == SymbolType.FLOW ||
symInfo.symType == SymbolType.LOOP)
{
isFloworLoop = true;
}
// if this is an operator
if(IsOperator(symInfo.symType))
{
boolean isFunction = false;
if(symInfo.symType ==
SymbolType.ENDOFSTATEMENT)
{
isFunction = CheckForFunction(i);
}
// else, default and switch should not be counted as they do not generate a new
path
if(!isFunction && symInfo.symbol !=
ParsingSymbol.ELSE && symInfo.symbol != ParsingSymbol.SWITCH &&
symInfo.symbol != ParsingSymbol.DEFAULT)
{
AddtoOperator(symInfo.lexeme);
}
}
// if this is an operand
else if(IsOperand(symInfo.symType))
{
boolean isFunction = false;
if(symInfo.symType == SymbolType.IDENTIFIER)
{
SymbolInfo sym =
(SymbolInfo)symTable.get(i+1);
if(sym.symType == SymbolType.METHODLP)
{
isFunction = true;
}
}

if(!isFunction)
{
AddtoOperand(symInfo.lexeme);
}
}
}
previousSymInfo = symInfo;
}
}
// function to process operand
private void AddtoOperand(String lexeme)
{
// increase N2
this.N2++;
// check if this is already in n2 list
if(!distinctOperandList.contains(lexeme))
{
// increase n2
this.n2++;
// add to n2 list
distOperand.add(lexeme);
distinctOperandList.add(lexeme);
}
}
// function to process operator
private void AddtoOperator(String lexeme)
{
// increase N1
this.N1++;
// check if this is already in n1 list
if(!distinctOperatorList.contains(lexeme))
{
// increase n1
this.n1++;
// add to n1 list
distOperator.add(lexeme);
distinctOperatorList.add(lexeme);
}
}
// check if we are currently processing a function call
private boolean CheckForFunction(int index)
{
SymbolInfo sym = (SymbolInfo)symTable.get(index-1);
boolean found = false;
if(sym.symType == SymbolType.METHODRP){
// now check, if this is actually a function call or its a construct
like in for loop
for(int i=index-1 ;i>0 ; i--)
{
sym = (SymbolInfo)symTable.get(i);
if(sym.symType == SymbolType.ENDOFSTATEMENT ||
sym.symType == SymbolType.ASSIGNMENT)
{
sym = (SymbolInfo)symTable.get(i+1);
found = true;
break;
}
}
// not a function call add to operator list
if(found && sym.symType == SymbolType.IDENTIFIER)
{
AddtoOperator(sym.lexeme);
}
}
return found;
}

// this print the results as per Halstead formula


public void PrintResults()
{
System.out.println("====================== Halstead Results
==========================");
System.out.println("Distinct Operators");
System.out.println(distOperator);
System.out.println("Distinct Operands");
System.out.println(distOperand);
System.out.println();
System.out.println("Number of unique (distinct) operators (n1) : " +
this.n1);
System.out.println("Number of unique (distinct) operands (n2) : " +
this.n2);
System.out.println("Total number of operators (N1) : " + this.N1);
System.out.println("Total number of operands (N2) : " + this.N2);
long N = this.N1 + this.N2;
System.out.println("Program length N = (N1 + N2) : " + N);
long n = this.n1 + this.n2;
System.out.println("Vocabulary size n = (n1 + n2) : " + n);
double V = N * (Math.log(n)/Math.log(2));
System.out.println("Program volume V = (N * log2(n)) : " + V);
double D = 0.0;
if(this.n2 > 0)
D = (( this.n1/2 )*(this.N2/this.n2));
System.out.println("Difficulty level D = (( n1/2 )*(N2/n2)) : " + D);
System.out.println("Effort to Implement E = V * D : " + (V * D));
System.out.println();

System.out.println("=============================================
=====================");
}
}

// AdjoinedHalstead.java

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/*
* this is the class to calculate adjoined halstead complexity.
*/
public class AdjoinedHalstead implements IComplexity{
// distinct operand
private ArrayList<String> distOperand;
// distinct operator
private ArrayList<String> distOperator;
// will keep adjoin constant if a value is assigned
private Integer constantAdjoin = 0;
// n1 , n2, N1 and N2 as per definition of adjoined Halstead
private long n1 = 0, n2 = 0, N1 = 0, N2 = 0;
// contain decision symbols for result printout
private Map<String, String> adjoinOperands;
// contain decision symbols for result printout
private Map<String, String> adjoinOperators;
// symbol table
private ArrayList<SymbolInfo> symTable;
// distinct operand
private ArrayList<String> distinctOperandList;
// distinct operator
private ArrayList<String> distinctOperatorList;
// constructor
public AdjoinedHalstead(ArrayList<SymbolInfo> symbolTable)
{
this.symTable = symbolTable;
this.distinctOperatorList = new ArrayList<String>();
this.distinctOperandList = new ArrayList<String>();
this.distOperand = new ArrayList<String>();
this.distOperator = new ArrayList<String>();
this.adjoinOperands = new HashMap<String, String>();
this.adjoinOperators = new HashMap<String, String>();
}

public AdjoinedHalstead(ArrayList<SymbolInfo> symbolTable, int


constAdjoin)
{
this(symbolTable);
this.constantAdjoin = constAdjoin;
}
// function to determine if this is an operand as per definition of Halstead
private boolean IsOperand(SymbolType sType)
{
return (sType == SymbolType.IDENTIFIER || sType ==
SymbolType.MAINMETHOD);
}
// function to determine if this is an operator as per definition of Halstead
private boolean IsOperator(SymbolType sType)
{
return (sType == SymbolType.SEPARATOR || sType ==
SymbolType.ENDOFSTATEMENT || sType == SymbolType.QUANTIFIER ||
sType == SymbolType.ASSIGNMENT || sType ==
SymbolType.CONDITION || sType == SymbolType.ARRAYLP
|| sType == SymbolType.FLOW || sType == SymbolType.LOOP
|| sType == SymbolType.OPERATOR || sType == SymbolType.RETURN );
}
// this method does the calculation
public void Calculate()
{
int count = symTable.size();
SymbolInfo symInfo;
SymbolInfo previousSymInfo = null;
// read symbol one by one from Symbol table
int scopeCounter = 0;
int funcCount = 0;
int adjoin = 0;
int nesting = 0;
boolean adjoinControlStructure = false;
boolean isFloworLoop = false;
for(int i=0;i<count;i++)
{
symInfo = (SymbolInfo)symTable.get(i);
if(symInfo.symType == SymbolType.MAINMETHOD)
{
// Adjoin will be zero as this symbol do not form control
structure hence no nesting complexity
AddtoOperator(symInfo.lexeme, 0);
}
if(symInfo.symType == SymbolType.SCOPE)
{
if(symInfo.symbol == ParsingSymbol.BEGIN)
{
// Adjoin will be zero as this symbol do not form
control structure hence no nesting complexity
AddtoOperator(symInfo.lexeme, 0);

scopeCounter++;
// check for start of function
if(!isFloworLoop && previousSymInfo!=null &&
previousSymInfo.symType == SymbolType.METHODRP)
{
funcCount++;
// this is to be reset for each module
adjoin = 0;
nesting = 0;
adjoinControlStructure = false;
distinctOperandList.clear();
//distinctOperatorList.clear();
}
}
else if(symInfo.symbol == ParsingSymbol.END)
{
scopeCounter--;
if(scopeCounter == 0)
{
isFloworLoop = false;
}
}
continue;
}
// if code block is inside a valid scope
if(scopeCounter>0 && funcCount>0)
{
if(symInfo.symType == SymbolType.FLOW ||
symInfo.symType == SymbolType.LOOP)
{
isFloworLoop = true;
}
// if adjoin control structure flag is ON and symbol is end
of paranthesis which marks the end of structure
// we should reset the adjoin and increase overall
nesting by one;
if(adjoinControlStructure && symInfo.symType ==
SymbolType.METHODRP)
{
adjoinControlStructure = false;
adjoin = 0;
nesting++;
}
// check and set new adjoin properly
// as per definition this is applicable only to if, case and
loop
// else, default and switch should not be counted as they
do not generate a new path
if((symInfo.symType == SymbolType.FLOW &&
symInfo.symbol != ParsingSymbol.SWITCH && symInfo.symbol !=
ParsingSymbol.ELSE &&

symInfo.symbol != ParsingSymbol.DEFAULT)
|| symInfo.symType == SymbolType.LOOP)
{
// new adjoin will be one more than current
nesting
adjoin = nesting + 1;
if(this.constantAdjoin > 0)
{
adjoin = this.constantAdjoin;
}
adjoinControlStructure = true;
}
// if this is an operator
if(IsOperator(symInfo.symType))
{
boolean isFunction = false;
if(symInfo.symType ==
SymbolType.ENDOFSTATEMENT)
{
isFunction = CheckForFunction(i, adjoin);
}
// else, default and switch should not be counted
as they do not generate a new path
if(!isFunction && symInfo.symbol !=
ParsingSymbol.ELSE && symInfo.symbol != ParsingSymbol.SWITCH &&
symInfo.symbol !=

ParsingSymbol.DEFAULT)
{
AddtoOperator(symInfo.lexeme, adjoin);
}
}
// if this is an operand
else if(IsOperand(symInfo.symType))
{
boolean isFunction = false;
if(symInfo.symType == SymbolType.IDENTIFIER)
{
SymbolInfo sym =
(SymbolInfo)symTable.get(i+1);
if(sym.symType == SymbolType.METHODLP)
{
isFunction = true;
}
}
// if this is not a function add to operand list along
with its adjoin
if(!isFunction)
{
AddtoOperand(symInfo.lexeme, adjoin);
}
}
}
previousSymInfo = symInfo;
}
}

// function to process operand along with adjoin for operand


private void AddtoOperand(String lexeme, int adjoin)
{
// increase N2 as per definition of adjoined halstead;
this.N2 = this.N2 + 1 + adjoin;
if(this.adjoinOperands.containsKey(lexeme))
{
this.adjoinOperands.put(lexeme,
this.adjoinOperands.get(lexeme) + " + " + adjoin);
}
else
{
this.adjoinOperands.put(lexeme, adjoin + "");
}
// check if this is already in n2 list
if(!distinctOperandList.contains(lexeme))
{
// increase n2
this.n2++;
// add to n2 list
distOperand.add(lexeme);
distinctOperandList.add(lexeme);
}
}
// function to process operator along with adjoin for operator
private void AddtoOperator(String lexeme, int adjoin)
{
// increase N1 as per definition of adjoined halstead;
this.N1 = this.N1 + 1 + adjoin;
if(this.adjoinOperators.containsKey(lexeme))
{
this.adjoinOperators.put(lexeme,
this.adjoinOperators.get(lexeme) + " + " + adjoin);
}
else
{
this.adjoinOperators.put(lexeme, adjoin + "");
}
// check if this is already in n1 list
if(!distinctOperatorList.contains(lexeme))
{
// increase n1
this.n1++;
// add to n1 list
distOperator.add(lexeme);
distinctOperatorList.add(lexeme);
}
}
// check if we are currently processing a function call
private boolean CheckForFunction(int index, int adjoin)
{
SymbolInfo sym = (SymbolInfo)symTable.get(index-1);
boolean found = false;
if(sym.symType == SymbolType.METHODRP){
// now check, if this is actually a function call or its a construct
like in for loop
for(int i=index-1 ;i>0 ; i--)
{
sym = (SymbolInfo)symTable.get(i);
if(sym.symType == SymbolType.ENDOFSTATEMENT ||
sym.symType == SymbolType.ASSIGNMENT)
{
sym = (SymbolInfo)symTable.get(i+1);
found = true;
break;
}
}
// not a function call add to operator list along with current
adjoin
// this adjoin is useful in FOR loop construct which has ; inside
construct
if(found && sym.symType == SymbolType.IDENTIFIER)
{
AddtoOperator(sym.lexeme, adjoin);
}
}
return found;
}
// this print the results as per adjoined Halstead formula
public void PrintResults()
{
if(this.constantAdjoin > 0)
{
System.out.println("======== Adjoined Halstead Results with
constant level ==========");
System.out.printf("constant level = %d \n",
this.constantAdjoin);
System.out.println();
}
else
{
System.out.println("================== Adjoined Halstead
Results =====================");
}
System.out.println("Distinct Operators");
System.out.println(distOperator);
System.out.println("Distinct Operands");
System.out.println(distOperand);
System.out.println();
System.out.println("Operators with adjoin (level)");
System.out.println(adjoinOperators);
System.out.println("Operands with adjoin (level)");
System.out.println(adjoinOperands);
System.out.println();
System.out.println("Number of unique (distinct) operators (n1) : " +
this.n1);
System.out.println("Number of unique (distinct) operands (n2) : " +
this.n2);
System.out.println("Total number of operators (N1) + cummulative
adjoin : " + this.N1);
System.out.println("Total number of operands (N2) + cummulative
adjoin : " + this.N2);
long N = this.N1 + this.N2;
System.out.println("Adjoined Program length N = (N1 + N2) : " + N);
long n = this.n1 + this.n2;
System.out.println("Adjoined Vocabulary size n = (n1 + n2) : " + n);
double V = N * (Math.log(n)/Math.log(2));
System.out.println("Adjoined Program volume V = (N * log2(n)) : " +
V);
double D = 0.0;
if(this.n2 > 0)
D = (( this.n1/2 )*(this.N2/this.n2));
System.out.println("Adjoined Difficulty level D = (( n1/2 )*(N2/n2)) : "
+ D);
System.out.println("Adjoined Effort to Implement E = V * D : " + (V *
D));
System.out.println();

System.out.println("=============================================
====================");
}
}
REFERENCES

1. Albert L. Baker, Stuart H. Zweben (1980), “A Comparison of Measures of


Control Flow Complexity”, IEEE Transactions On Software Engineering, Vol.
SE-6.

2. Everald E. Mills. (1988), “Software Metrics”, SEI Curriculum Module SEI-CM-


12-1.1

3. Howard A. Jensen, K. Vairavan (1985), “An Experimental Study of Software


Metrics for Real-Time Software”, IEEE Transactions On Software
Engineering, Vol. SE-11, No. 2.

4. Jarrett Rosenberg (1997), “Some Misconceptions About Lines of Code”, 0-


8186-8093-8/97 IEEE.

5. John John Michura & Miriam A. M. Capretz (2005), “Metrics Suite for Class
Complexity”, Volume 2, 4-6 April 2005 Page(s):404 - 409 Vol. 2 ,
Information Technology: Coding and Computing.

6. The Software Engineering Institute (SEI)(2008), “Software Technology


Roadmap”, Carnegie Mellon University.

You might also like