package org.projectusus.core.filerelations.model; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import net.sourceforge.c4j.ContractReference; import org.eclipse.core.resources.IFile; import org.projectusus.core.util.ForEach2; import org.projectusus.core.util.SelectUnique; import ch.akuhn.foreach.ForEach; @ContractReference( contractClassName = "ClassDescriptorContract" ) public class ClassDescriptor { private static Map<ClassDescriptorKey, ClassDescriptor> classes = new HashMap<ClassDescriptorKey, ClassDescriptor>(); protected final Set<ClassDescriptor> parents; protected final Set<ClassDescriptor> children; private final ClassDescriptorKey key; private final Set<ClassDescriptor> transitiveChildrenCache = new HashSet<ClassDescriptor>(); public static void clear() { classes.clear(); } public static Set<ClassDescriptor> getAll() { return new HashSet<ClassDescriptor>( classes.values() ); } public static ClassDescriptor of( WrappedTypeBinding type ) { return ClassDescriptor.of( new ClassDescriptorKey( type ) ); } public static ClassDescriptor of( IFile file, Classname classname, Packagename packagename ) { return ClassDescriptor.of( new ClassDescriptorKey( file, classname, packagename ) ); } private static ClassDescriptor of( ClassDescriptorKey key ) { if( classes.containsKey( key ) ) { return classes.get( key ); } return newClassDescriptor( key ); } private static ClassDescriptor newClassDescriptor( ClassDescriptorKey key ) { ClassDescriptor newClassDescriptor = new ClassDescriptor( key ); classes.put( key, newClassDescriptor ); return newClassDescriptor; } private static void removeDescriptor( ClassDescriptorKey key ) { ClassDescriptor descriptor = classes.remove( key ); if( descriptor != null ) { descriptor.removeFromPackage(); } } private ClassDescriptor( ClassDescriptorKey key ) { this.key = key; this.parents = new HashSet<ClassDescriptor>(); this.children = new HashSet<ClassDescriptor>(); key.getPackagename().addClass( this ); } private void removeFromPackage() { key.getPackagename().removeClass( this ); } public IFile getFile() { return key.getFile(); } public Classname getClassname() { return key.getClassname(); } public Packagename getPackagename() { return key.getPackagename(); } @Override public boolean equals( Object object ) { // equality does not work! file can change... return object instanceof ClassDescriptor && equals( (ClassDescriptor)object ); } @Override public int hashCode() { return key.hashCode(); } private boolean equals( ClassDescriptor other ) { return key.equals( other.key ); } @Override public String toString() { return qualifiedClassName() + "[" + key.getFile().getName() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ } public String qualifiedClassName() { return key.getPackagename() + "." + key.getClassname(); //$NON-NLS-1$ } private void clearOwnAndParentsTransitiveChildrenCache() { if( transitiveChildrenCache.isEmpty() ) { return; } transitiveChildrenCache.clear(); for( ClassDescriptor parent : parents ) { parent.clearOwnAndParentsTransitiveChildrenCache(); } } public Set<ClassDescriptor> getChildren() { return Collections.unmodifiableSet( children ); } public Set<ClassDescriptor> getParents() { return Collections.unmodifiableSet( parents ); } public Set<ClassDescriptor> getChildrenInOtherPackages() { for( SelectUnique<ClassDescriptor> target : ForEach2.selectUnique( children ) ) { target.yield = !this.getPackagename().equals( target.value.getPackagename() ); } return ForEach.result(); } public Set<ClassDescriptor> getChildrenInSamePackage() { for( SelectUnique<ClassDescriptor> target : ForEach2.selectUnique( children ) ) { target.yield = this.getPackagename().equals( target.value.getPackagename() ); } return ForEach.result(); } private Set<ClassDescriptor> getTransitiveChildren() { if( transitiveChildrenCache.isEmpty() ) { collectTransitiveChildren( transitiveChildrenCache ); } return transitiveChildrenCache; } private void collectTransitiveChildren( Set<ClassDescriptor> result ) { result.add( this ); for( ClassDescriptor child : getChildren() ) { if( !result.contains( child ) ) { child.collectTransitiveChildren( result ); } } } private Set<ClassDescriptor> getTransitiveParents() { Set<ClassDescriptor> result = new HashSet<ClassDescriptor>(); collectTransitiveParents( result ); return result; } private void collectTransitiveParents( Set<ClassDescriptor> result ) { result.add( this ); for( ClassDescriptor parent : parents ) { if( !result.contains( parent ) ) { parent.collectTransitiveParents( result ); } } } public int getCCD() { return getTransitiveChildren().size(); } public int getTransitiveParentCount() { return getTransitiveParents().size(); } public void prepareRemoval() { markAndRemoveAllOutgoingRelations(); ClassDescriptorCleanup.registerForCleanup( this ); } private void markAndRemoveAllOutgoingRelations() { for( ClassDescriptor target : children ) { target.parents.remove( this ); } children.clear(); clearOwnAndParentsTransitiveChildrenCache(); } public void removeFromPool() { clearOwnAndParentsTransitiveChildrenCache(); for( ClassDescriptor source : parents ) { source.children.remove( this ); } parents.clear(); // also for outgoings, but we are only called when the outgoings are emptied already removeDescriptor( this.key ); } public void addChild( ClassDescriptor target ) { if( !this.equals( target ) ) { children.add( target ); target.parents.add( this ); clearOwnAndParentsTransitiveChildrenCache(); } } public Set<ClassDescriptor> getParentsInOtherPackages() { Set<ClassDescriptor> result = new HashSet<ClassDescriptor>(); for( ClassDescriptor parent : parents ) { if( !this.getPackagename().equals( parent.getPackagename() ) ) { result.add( parent ); } } return result; } }