/*
* JCarder -- cards Java programs to keep threads disentangled
*
* Copyright (C) 2006-2007 Enea AB
* Copyright (C) 2007 Ulrik Svensson
* Copyright (C) 2007 Joel Rosdahl
*
* This program is made available under the GNU GPL version 2, with a special
* exception for linking with JUnit. See the accompanying file LICENSE.txt for
* details.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE.
*/
package com.enea.jcarder.analyzer;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import com.enea.jcarder.common.contexts.ContextReaderIfc;
/**
* An instance of this class represents a single cycle of edges. The edges must
* form a single circle witout any alternative paths. If there are alternative
* paths in a graph cycle, those cycles will be split into separate Cycle
* objects.
*
* TODO Write basic tests.
*/
class Cycle {
final HashSet<LockEdge> mEdgesInCycle = new HashSet<LockEdge>();
Cycle(Collection<LockEdge> edgesInTheCycle) {
mEdgesInCycle.addAll(edgesInTheCycle);
assert mEdgesInCycle.size() >= 2;
}
HashSet<LockEdge> getEdges() {
return mEdgesInCycle;
}
HashSet<LockNode> getNodes() {
HashSet<LockNode> nodes = new HashSet<LockNode>();
for (LockEdge edge : mEdgesInCycle) {
/*
* All sources will be included if we get all the targets, since it
* is a cycle.
*/
nodes.add(edge.getTarget());
}
return nodes;
}
void updateNodeCycleStatus() {
final LockNode.CycleType type;
if (isSingleThreaded()) {
type = LockNode.CycleType.SINGLE_THREADED_CYCLE;
} else {
type = LockNode.CycleType.CYCLE;
}
for (LockEdge edge : mEdgesInCycle) {
edge.getSource().raiseCycleType(type);
edge.getTarget().raiseCycleType(type);
}
}
boolean isSingleThreaded() {
// TODO Cache the result to improve performance?
final Iterator<LockEdge> iter = mEdgesInCycle.iterator();
if (iter.hasNext()) {
final long firstThreadId = iter.next().getThreadId();
while (iter.hasNext()) {
if (firstThreadId != iter.next().getThreadId()) {
return false;
}
}
}
return true;
}
boolean alike(Cycle other, ContextReaderIfc reader) {
if (this.equals(other)) {
return true;
}
if (mEdgesInCycle.size() != other.mEdgesInCycle.size()) {
return false;
}
if (isSingleThreaded() != other.isSingleThreaded()) {
return false;
}
LinkedList<LockEdge> otherEdges =
new LinkedList<LockEdge>(other.mEdgesInCycle);
// TODO Refactor the following code?
outerLoop:
for (LockEdge edge : mEdgesInCycle) {
Iterator<LockEdge> iter = otherEdges.iterator();
while (iter.hasNext()) {
if (edge.alike(iter.next(), reader)) {
iter.remove();
continue outerLoop;
}
}
return false;
}
return true;
}
public boolean equals(Object obj) {
try {
Cycle other = (Cycle) obj;
return mEdgesInCycle.equals(other.mEdgesInCycle);
} catch (ClassCastException e) {
return false;
}
}
public int hashCode() {
return mEdgesInCycle.hashCode();
}
public String toString() {
return mEdgesInCycle.toString();
}
}