package jdepend.model;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import jdepend.framework.log.LogUtil;
import jdepend.framework.util.MetricsFormat;
import jdepend.metadata.JavaClass;
import jdepend.metadata.JavaClassRelationItem;
import jdepend.metadata.JavaPackage;
import jdepend.model.component.JavaPackageComponent;
import jdepend.model.component.PackageSubJDependUnit;
import jdepend.model.component.VirtualComponent;
import jdepend.model.profile.ProfileException;
import jdepend.model.profile.model.ComponentProfile;
import jdepend.model.profile.model.RelationProfile;
import jdepend.model.result.AnalysisResult;
import jdepend.model.util.ComponentPathSegment;
import jdepend.model.util.JavaClassUnitUtil;
import jdepend.model.util.RelationCreator;
/**
* 组件抽象类
*
* 该抽象类主要代表可以进行指标定义的Component
*
* 它也承担将JavaPackage集合聚集成Component的职责
*
* @author <b>Abner</b>
*
*/
public abstract class Component extends AbstractSubJDependUnit {
/**
*
*/
private static final long serialVersionUID = 3010412682448187877L;
private String title;
private int layer;
protected List<JavaClassUnit> javaClasses = new ArrayList<JavaClassUnit>();
private transient AnalysisResult result;
private transient AreaComponent areaComponent;// 所属组件区域,由识别设计动机模块计算得到
protected transient Map<String, JavaClassUnit> javaClassesForId = new HashMap<String, JavaClassUnit>();// 缓存
protected transient Collection<Component> afferents = null;// 缓存
protected transient Collection<Component> efferents = null;// 缓存
private transient Collection<JavaPackage> javaPackages = null;// 缓存
private transient String path = null;// 缓存
private transient Float cohesion = null;
private transient Float caCoupling = null;
private transient Float ceCoupling = null;
private transient Collection<Relation> relations = new ArrayList<Relation>();
private transient Collection<? extends SubJDependUnit> subJDependUnits = null;// 缓存
public static final int UndefinedComponentLevel = 0;
public static final int PlatformComponentLevel = 1;
public static final String PlatformComponentLevelDesc = "平台组件";
public static final int DomainComponentLevel = 2;
public static final String DomainComponentLevelDesc = "领域业务组件";
public static final int AppComponentLevel = 3;
public static final String AppComponentLevelDesc = "应用业务组件";
public static final int InteractiveComponentLevel = 4;
public static final String InteractiveComponentLevelDesc = "交互组件";
public static final String MutabilityType = "MutabilityType";
public static final String MiddleType = "MiddleType";
public static final String StableType = "StableType";
public static final String Area = "Area";
public Component() {
}
public Component(String name) {
super(name);
}
public Component(Component component) {
super(component);
this.title = component.title;
this.layer = component.layer;
this.javaClasses = component.javaClasses;
this.result = component.result;
this.areaComponent = component.areaComponent;
this.javaClassesForId = component.javaClassesForId;
this.afferents = component.afferents;
this.efferents = component.efferents;
this.javaPackages = component.javaPackages;
this.path = component.path;
this.cohesion = component.cohesion;
this.caCoupling = component.caCoupling;
this.ceCoupling = component.ceCoupling;
this.relations = component.relations;
this.subJDependUnits = component.subJDependUnits;
}
/**
* 初始化
*
* 该方法将在客户端初始化Command时调用执行
*
* @param group
* @param command
* @param info
* @throws ComponentException
*/
public void init(String group, String command, String info) throws ComponentException {
}
/**
* 得到Component列表
*
* 在联机模式下,该方法将在服务器端执行
*
* @param javaPackages
* @return
* @throws ComponentException
*/
public final List<Component> list(Collection<JavaPackage> javaPackages) throws ComponentException {
List<Component> components = this.doList(javaPackages);
this.filterExternalJavaClass(components);
return components;
}
/**
* 过滤掉不被组件包含的JavaClass
*
* @param components
*/
protected void filterExternalJavaClass(Collection<Component> components) {
Collection<JavaClass> javaClasses = JavaClassUnitUtil.getAllClasses(components);
for (JavaClass javaClass : javaClasses) {
javaClass.filterExternalJavaClass(javaClasses);
}
}
/**
* 每个Component需要装载上包含的javaPackages。
*
* @param javaPackages
* @return
* @throws ComponentException
*/
protected abstract List<Component> doList(Collection<JavaPackage> javaPackages) throws ComponentException;
@Override
public boolean isInner() {
for (JavaClassUnit javaClass : this.getClasses()) {
if (javaClass.isInner()) {
return true;
}
}
return false;
}
@Override
public Collection<JavaClassUnit> getClasses() {
return this.javaClasses;
}
public JavaClassUnit getTheClass(String classId) {
return this.javaClassesForId.get(classId);
}
@Override
public synchronized Collection<JavaPackage> getJavaPackages() {
if (this.javaPackages == null) {
this.javaPackages = new HashSet<JavaPackage>();
for (JavaClassUnit javaClass : this.javaClasses) {
this.javaPackages.add(javaClass.getJavaClass().getJavaPackage());
}
}
return this.javaPackages;
}
@Override
public synchronized String getPath() {
if (path == null) {
// 获得指定单元的包列表
List<String> javaPackageNames = new ArrayList<String>();
for (JavaPackage javaPackage : this.getJavaPackages()) {
javaPackageNames.add(javaPackage.getName());
}
path = Component.getDefaultComponentName(javaPackageNames, true);
}
return path;
}
/**
* 建立组件与类之间的双向关联
*
* @param javaClass
*/
public synchronized void addJavaClass(JavaClassUnit javaClass) {
if (!this.javaClasses.contains(javaClass)) {
javaClass.setComponent(this);
this.javaClasses.add(javaClass);
this.javaClassesForId.put(javaClass.getId(), javaClass);
// 将不在包中的类增加到包中
if (javaClass.getJavaClass().getJavaPackage() == null) {
if (this.getJavaPackages().size() > 0) {
this.getJavaPackages().iterator().next().addClass(javaClass.getJavaClass());
}
}
}
}
public synchronized boolean removeJavaClass(JavaClassUnit javaClass) {
if (this.javaClasses.remove(javaClass)) {
if (javaClass.getComponent().equals(this)) {
javaClass.setComponent(null);
// 从类所在的包中删除该类
javaClass.getJavaClass().getJavaPackage().removeClass(javaClass.getJavaClass());
}
this.javaClassesForId.remove(javaClass.getId());
return true;
} else {
return false;
}
}
@Override
public boolean containsClass(JavaClassUnit javaClass) {
return this.containsClass(javaClass.getJavaClass());
}
public boolean containsClass(JavaClass javaClass) {
// javaClass和JavaClassUnit共用一个Id
if (javaClass.isInnerClass()) {
JavaClass hostClass = javaClass.getHostClass();
if (hostClass != null) {
return this.javaClassesForId.containsKey(hostClass.getId());
} else {
return this.javaClassesForId.containsKey(javaClass.getId());
}
} else {
return this.javaClassesForId.containsKey(javaClass.getId());
}
}
@Override
public int getClassCount() {
return this.getClasses().size();
}
@Override
public int getAbstractClassCount() {
int rtn = 0;
for (JavaClassUnit javaClass : this.getClasses()) {
rtn += javaClass.getAbstractClassCount();
}
return rtn;
}
@Override
public int getConcreteClassCount() {
return this.getClassCount() - this.getAbstractClassCount();
}
@Override
public synchronized Collection<Component> getAfferents() {
if (this.afferents == null) {
this.afferents = new HashSet<Component>();
for (Relation relation : relations) {
if (relation.getDepend().getComponent().equals(this)) {
this.afferents.add(relation.getCurrent().getComponent());
}
}
}
return this.afferents;
}
@Override
public synchronized Collection<Component> getEfferents() {
if (this.efferents == null) {
this.efferents = new HashSet<Component>();
for (Relation relation : relations) {
if (relation.getCurrent().getComponent().equals(this)) {
this.efferents.add(relation.getDepend().getComponent());
}
}
}
return this.efferents;
}
public boolean isRelation(Component component) {
return this.getEfferents().contains(component) || this.getAfferents().contains(component);
}
@Override
public RelationDetail ceCouplingDetail(JDependUnit dependUnit) {
RelationDetail detail = new RelationDetail();
if (this.equals(dependUnit)) {
return detail;
}
for (Relation relation : this.relations) {
if (relation.getDepend().getComponent().equals(dependUnit)) {
return relation.getDetail();
}
}
float intensity = 0;
for (JavaClassUnit javaClass : this.getClasses()) {
RelationDetail relationDetail = javaClass.ceCouplingDetail(dependUnit);
intensity += relationDetail.getIntensity();
detail.addItems(relationDetail.getItems());
}
detail.setIntensity(intensity);
return detail;
}
@Override
public RelationDetail caCouplingDetail(JDependUnit dependUnit) {
RelationDetail detail = new RelationDetail();
if (this.equals(dependUnit)) {
return detail;
}
for (Relation relation : this.relations) {
if (relation.getCurrent().getComponent().equals(dependUnit)) {
return relation.getDetail();
}
}
float intensity = 0;
for (JavaClassUnit javaClass : this.getClasses()) {
RelationDetail relationDetail = javaClass.caCouplingDetail(dependUnit);
intensity += relationDetail.getIntensity();
detail.addItems(relationDetail.getItems());
}
detail.setIntensity(intensity);
return detail;
}
/**
* 计算与特定组件的传出耦合细节信息
*
* @param jDependUnit
* @return
*/
public RelationDetail calCeCouplingDetail(Component component) {
RelationDetail detail = new RelationDetail();
if (this.equals(component)) {
return detail;
}
float intensity = 0;
for (JavaClassUnit javaClass : this.getClasses()) {
for (JavaClassRelationItem relationItem : javaClass.getJavaClass().getCeItems()) {
if (component.containsClass(relationItem.getTarget())) {
detail.addItem(relationItem);
intensity += relationItem.getRelationIntensity();
}
}
}
detail.setIntensity(intensity);
return detail;
}
public Relation getCeTheRelation(Component component) {
for (Relation relation : this.relations) {
if (relation.getDepend().getComponent().equals(component)) {
return relation;
}
}
return null;
}
public Relation getCaTheRelation(Component component) {
for (Relation relation : this.relations) {
if (relation.getCurrent().getComponent().equals(component)) {
return relation;
}
}
return null;
}
public Collection<Component> getRelationComponents() {
Collection<Component> relationComponents = new HashSet<Component>();
for (Relation relation : this.relations) {
if (relation.getCurrent().getComponent().equals(this)) {
relationComponents.add(relation.getDepend().getComponent());
} else {
relationComponents.add(relation.getCurrent().getComponent());
}
}
return relationComponents;
}
@Override
public synchronized float caCoupling() {
if (caCoupling == null) {
float intensity = 0;
for (Relation relation : this.relations) {
if (relation.getDepend().getComponent().equals(this)) {
intensity += relation.getIntensity();
}
}
this.caCoupling = intensity;
}
return this.caCoupling;
}
@Override
public synchronized float ceCoupling() {
if (ceCoupling == null) {
float intensity = 0;
for (Relation relation : this.relations) {
if (relation.getCurrent().getComponent().equals(this)) {
intensity += relation.getIntensity();
}
}
this.ceCoupling = intensity;
}
return this.ceCoupling;
}
public synchronized void addRelation(Relation relation) {
if (!this.relations.contains(relation)) {
this.relations.add(relation);
}
}
public Collection<Relation> getRelations() {
return this.relations;
}
public Collection<Relation> getAfferentRelations() {
Collection<Relation> relations = new HashSet<Relation>();
for (Relation relation : this.relations) {
if (relation.getDepend().getComponent().equals(this)) {
relations.add(relation);
}
}
return relations;
}
public Collection<Relation> getEfferentRelations() {
Collection<Relation> relations = new HashSet<Relation>();
for (Relation relation : this.relations) {
if (relation.getCurrent().getComponent().equals(this)) {
relations.add(relation);
}
}
return relations;
}
public boolean stability(Component component) {
RelationProfile relationProfile = this.getResult().getRunningContext().getProfileFacade().getRelationProfile();
return this.getStability() + relationProfile.getSDPDifference() < component.getStability();
}
public Collection<Relation> open() {
Collection<Relation> allRelations = new HashSet<Relation>();
Collection<Component> innerComponents = new HashSet<Component>();
Component virtualComponent;
for (JavaClassUnit javaClass : this.getClasses()) {
virtualComponent = new VirtualComponent(javaClass);
innerComponents.add(virtualComponent);
}
Collection<Relation> innerRelations = new RelationCreator().create(innerComponents);
allRelations.addAll(innerRelations);
Collection<Component> outerComponents = this.getRelationComponents();
Collection<Relation> ceRelations = new RelationCreator(true, false).create(innerComponents, outerComponents);
allRelations.addAll(ceRelations);
Collection<Relation> caRelations = new RelationCreator(false, true).create(outerComponents, innerComponents);
allRelations.addAll(caRelations);
return allRelations;
}
@Override
protected GroupInfoCalculator createGroupInfoCalculator() {
throw new RuntimeException("子类需要复写该方法");
}
@Override
public void clear() {
super.clear();
this.afferents = null;
this.efferents = null;
this.javaPackages = null;
this.path = null;
this.cohesion = null;
this.caCoupling = null;
this.ceCoupling = null;
this.relations = new ArrayList<Relation>();
this.subJDependUnits = null;
this.areaComponent = null;
}
@Override
public synchronized float getCohesion() {
if (cohesion == null) {
float intensity = 0;
for (JavaClassUnit javaClass : this.getClasses()) {
intensity += javaClass.getCohesion();
}
cohesion = intensity / 2;
}
return cohesion;
}
@Override
public Float getBalance() {
if (this.getCoupling() == 0) {
return null;
}
try {
Collection<? extends SubJDependUnit> subJDependUnits = this.getSubJDependUnits();
if (subJDependUnits.size() > 0) {
if (subJDependUnits.size() == 1) {
return 1F;
} else {
float balance = 0F;
for (SubJDependUnit subJDependUnit : subJDependUnits) {
balance += subJDependUnit.getBalance();
}
return balance / subJDependUnits.size();
}
} else {
return 0F;
}
} catch (ProfileException e) {
e.printStackTrace();
return 0F;
}
}
/**
* 获取用于计算内聚性的子元素集合
*
* @return
* @throws ProfileException
*/
public Collection<? extends SubJDependUnit> getSubJDependUnits() throws ProfileException {
if (this.subJDependUnits == null) {
ComponentProfile componentProfile = this.getResult().getRunningContext().getProfileFacade()
.getComponentProfile();
if (componentProfile.getBalance().equals(ComponentProfile.balanceFromPackage)) {
if (this.getJavaPackages().size() == 0 || this.getJavaPackages().size() == 1) {
this.subJDependUnits = this.javaClasses;
} else {
Collection<PackageSubJDependUnit> packageComponents = new HashSet<PackageSubJDependUnit>();
for (JavaPackage javaPackage : this.getJavaPackages()) {
packageComponents.add(new PackageSubJDependUnit(javaPackage, this.getResult()));
}
Collection<Component> outerComponents = this.getRelationComponents();
new RelationCreator().create(packageComponents);
new RelationCreator(false, true).create(outerComponents, packageComponents);
new RelationCreator(true, false).create(packageComponents, outerComponents);
this.subJDependUnits = packageComponents;
}
} else if (componentProfile.getBalance().equals(ComponentProfile.balanceFromClass)) {
this.subJDependUnits = this.javaClasses;
} else {
throw new ProfileException("用于生成内聚性的组件配置信息错误,balance=" + componentProfile.getBalance());
}
}
return this.subJDependUnits;
}
public SubJDependUnit getTheSubJDependUnit(String name) throws ComponentException {
try {
for (SubJDependUnit subJDependUnit : this.getSubJDependUnits()) {
if (subJDependUnit.getName().equals(name)) {
return subJDependUnit;
}
}
return null;
} catch (ProfileException e) {
throw new ComponentException(e);
}
}
public Component clone(Map<String, JavaClassUnit> javaClasses) throws ComponentException {
try {
Component obj = this.getClass().newInstance();
obj.setName(this.getName());
obj.setTitle(this.getTitle());
obj.setType(this.getType());
obj.setLayer(this.getLayer());
obj.setAreaComponent(this.getAreaComponent());
for (JavaClassUnit javaClass : this.javaClasses) {
obj.addJavaClass(javaClasses.get(javaClass.getId()));
}
return obj;
} catch (Exception e) {
e.printStackTrace();
throw new ComponentException("克隆" + this.getName() + "出错", e);
}
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getLayer() {
return layer;
}
public void setLayer(int layer) {
this.layer = layer;
}
public boolean isDefinedComponentLevel() {
return this.layer != UndefinedComponentLevel;
}
public String getLayerDesc() {
return layerDesc(this.layer);
}
public static String layerDesc(int layer) {
if (layer == jdepend.model.Component.UndefinedComponentLevel) {
return null;
} else if (layer == DomainComponentLevel) {
return DomainComponentLevelDesc;
} else if (layer == AppComponentLevel) {
return AppComponentLevelDesc;
} else if (layer == PlatformComponentLevel) {
return PlatformComponentLevelDesc;
} else if (layer == InteractiveComponentLevel) {
return InteractiveComponentLevelDesc;
} else {
return null;
}
}
public AreaComponent getAreaComponent() {
return areaComponent;
}
public void setAreaComponent(AreaComponent areaComponent) {
this.areaComponent = areaComponent;
}
public void setResult(AnalysisResult result) {
this.result = result;
}
@Override
public AnalysisResult getResult() {
return this.result;
}
public static Component getDefaultComponent() {
return new JavaPackageComponent();
}
@Override
public int collectCycle(List<JDependUnit> list, Map<JDependUnit, Integer> knowledge) {
if (list.size() > 20) {
LogUtil.getInstance(JavaClassUnit.class).systemWarning(
"Component[" + list.get(0).getName() + "][" + this.getName() + "] collectCycle 搜索深度大于20停止搜索");
return StopCheckCycle;// 搜索深度大于20时停止
}
if (list.contains(this)) {
if (list.get(0).equals(this)) {
return Cycle;// 存在循环依赖
} else {
// 通知其他组件存在循环依赖
int index;
L: for (index = 1; index < list.size(); index++) {
if (list.get(index).equals(this)) {
break L;
}
}
List<Component> otherCycles = new ArrayList<Component>();
for (int pos = index; pos < list.size(); pos++) {
otherCycles.add((Component) list.get(pos));
}
for (Component unit : otherCycles) {
unit.setCycles(otherCycles);
}
knowledge.put(this, LocalCycle);
return LocalCycle;// 存在局部循环依赖
}
}
list.add(this);// 将当前分析单元入栈
if (this.getEfferents().contains(list.get(0))) {// 直接依赖进行广度搜索
return Cycle;
}
L: for (Component efferent : this.getEfferents()) {
Integer rtnInteger = (Integer) knowledge.get(efferent);// 获取历史扫描数据
if (rtnInteger == null) {// 没有扫描过的区域进行深度扫描
int rtn = efferent.collectCycle(list, knowledge);// 深度搜索该区域
if (rtn == Cycle) {// 存在循环依赖
// 通知其他组件存在循环依赖
for (int index = 1; index < list.size(); index++) {
((Component) list.get(index)).setCycles(list);
}
return Cycle;
} else if (rtn == StopCheckCycle) {
break L;// 搜索深度大于20时停止
}
}
}
list.remove(this);// 将当前分析单元出栈
knowledge.put(this, NoCycle);// 记录该对象扫描过的结果
return NoCycle;// 不存在循环依赖
}
@Override
public Object getValue(String metrics) {
if (metrics.equals(Area)) {
return this.getAreaComponent();
} else {
return super.getValue(metrics);
}
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
this.relations = new ArrayList<Relation>();
this.javaClassesForId = new HashMap<String, JavaClassUnit>();
for (JavaClassUnit javaClass : this.javaClasses) {
this.javaClassesForId.put(javaClass.getId(), javaClass);
}
}
@Override
public String toString() {
return "名称:" + this.getName() + "标题:" + this.getTitle() + " 是否内部:" + this.isInner() + " 类总数:"
+ this.getClassCount() + " 具体类:" + this.getConcreteClassCount() + " 抽象类:"
+ this.getAbstractClassCount() + " 总行数:" + this.getLineCount() + " 传入:" + this.getAfferentCoupling()
+ " 传出:" + this.getEfferentCoupling() + " 抽象程度:"
+ MetricsFormat.toFormattedMetrics(this.getAbstractness()) + " 稳定性:"
+ MetricsFormat.toFormattedMetrics(this.getStability()) + " 合理性:"
+ MetricsFormat.toFormattedMetrics(this.getDistance()) + " 耦合值:"
+ MetricsFormat.toFormattedMetrics(this.getCoupling()) + " 内聚值:"
+ MetricsFormat.toFormattedMetrics(this.getCohesion()) + " 内聚性:"
+ MetricsFormat.toFormattedMetrics(this.getBalance()) + " 封装性:"
+ (this.getEncapsulation() == null ? "-" : MetricsFormat.toFormattedMetrics(this.getEncapsulation()));
}
public static String getDefaultComponentName(List<String> javaPackages, boolean isFullComponentName) {
if (javaPackages.size() == 1) {
if (isFullComponentName) {
return javaPackages.get(0);
} else {
return javaPackages.get(0).substring(javaPackages.get(0).lastIndexOf(".") + 1);
}
} else {
List<ComponentPathSegment> segments = ComponentPathSegment.create(javaPackages);
String oldSegment = "";
String oldPath = "";
L: for (ComponentPathSegment segment : segments) {
if (segment.getCount() < javaPackages.size()) {
break L;
}
oldSegment = segment.getName();
oldPath = oldPath + "." + segment.getName();
}
if (isFullComponentName) {
if (oldPath.length() == 0) {
return oldPath;
} else {
return oldPath.substring(1);
}
} else {
return oldSegment;
}
}
}
}