/*
* 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.Map;
import net.jcip.annotations.NotThreadSafe;
import com.enea.jcarder.common.LockingContext;
import com.enea.jcarder.common.contexts.ContextReaderIfc;
/**
* A LockEdge instance represents a directed edge from a source LockNode to a
* target LockNode.
*/
@NotThreadSafe
class LockEdge {
private final LockNode mSource;
private final LockNode mTarget;
private final long mThreadId; // The thread that did the synchronization.
private int mSourceContextId;
private int mTargetContextId;
private long mNumberOfDuplicates;
LockEdge(LockNode source,
LockNode target,
long threadId,
int sourceLockingContextId,
int targetLockingContextId) {
mSource = source;
mTarget = target;
mThreadId = threadId;
mSourceContextId = sourceLockingContextId;
mTargetContextId = targetLockingContextId;
mNumberOfDuplicates = 0;
}
void merge(LockEdge other) {
assert this.equals(other);
mNumberOfDuplicates += (other.mNumberOfDuplicates + 1);
}
long getDuplicates() {
return mNumberOfDuplicates;
}
boolean alike(LockEdge other, ContextReaderIfc reader) {
/*
* TODO Some kind of cache to improve performance? Note that the context
* IDs are not declared final.
*/
LockingContext thisSourceContext =
reader.readContext(mSourceContextId);
LockingContext otherSourceContext =
reader.readContext(other.mSourceContextId);
LockingContext thisTargetContext =
reader.readContext(mTargetContextId);
LockingContext otherTargetContext =
reader.readContext(other.mTargetContextId);
return thisSourceContext.alike(otherSourceContext)
&& thisTargetContext.alike(otherTargetContext)
&& mSource.alike(other.mSource, reader)
&& mTarget.alike(other.mTarget, reader);
}
public boolean equals(Object obj) {
/*
* TODO It might be a potential problem to use LockEdges in HashMaps
* since they are mutable and this equals method depends on them?
*/
try {
LockEdge other = (LockEdge) obj;
return (mTarget.getLockId() == other.mTarget.getLockId())
&& (mSource.getLockId() == other.mSource.getLockId())
&& (mThreadId == other.mThreadId)
&& (mSourceContextId == other.mSourceContextId)
&& (mTargetContextId == other.mTargetContextId);
} catch (Exception e) {
return false;
}
}
public int hashCode() {
// TODO Improve hashCode algorithm to improve performance?
return mTarget.getLockId() + mSource.getLockId();
}
LockNode getTarget() {
return mTarget;
}
LockNode getSource() {
return mSource;
}
int getSourceLockingContextId() {
return mSourceContextId;
}
int getTargetLockingContextId() {
return mTargetContextId;
}
/**
* Translate the source and target context ID according to a translation
* map.
*/
void translateContextIds(Map<Integer, Integer> translation) {
final Integer newSourceId = translation.get(mSourceContextId);
if (newSourceId != null && newSourceId != mSourceContextId) {
mSourceContextId = newSourceId;
}
final Integer newTargetId = translation.get(mTargetContextId);
if (newTargetId != null && newSourceId != mTargetContextId) {
mTargetContextId = newTargetId;
}
}
long getThreadId() {
return mThreadId;
}
public String toString() {
return " " + mSource + "->" + mTarget;
}
}