package org.bundlemaker.core.jtype.internal; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import org.bundlemaker.core.common.collections.GenericCache; import org.bundlemaker.core.jtype.IReference; import org.bundlemaker.core.jtype.IType; import org.bundlemaker.core.jtype.ITypeModularizedSystem; import org.bundlemaker.core.jtype.ITypeModule; import org.bundlemaker.core.jtype.ITypeResource; import org.bundlemaker.core.jtype.ITypeSelector; import org.bundlemaker.core.project.IMovableUnit; import org.bundlemaker.core.project.IProjectContentResource; import org.bundlemaker.core.resource.IModule; import org.bundlemaker.core.resource.IModuleResource; import org.bundlemaker.core.resource.IModuleAwareMovableUnit; import org.bundlemaker.core.spi.modext.ICacheAwareModularizedSystem; import org.bundlemaker.core.spi.modext.ICacheCallback; import org.eclipse.core.runtime.Assert; public class TypeModularizedSystem implements ITypeModularizedSystem, ICacheCallback { /** type name -> type */ private GenericCache<String, Set<IType>> _typeNameToTypeCache; /** type name -> referring type */ private GenericCache<String, Set<IType>> _typeNameToReferringCache; /** type -> module */ private GenericCache<IType, Set<IModule>> _typeToModuleCache; /** - */ private List<ITypeSelector> _moduleSelectors; /** - */ private DefaultTypeSelector _defaultTypeSelector; /** * <p> * Creates a new instance of type {@link AbstractQueryableModularizedSystem}. * </p> * * @param name * @param system */ public TypeModularizedSystem(ICacheAwareModularizedSystem system) { // _moduleSelectors = new LinkedList<ITypeSelector>(); // _defaultTypeSelector = new DefaultTypeSelector(system.getBundleMakerProject().getProjectDescription()); // _defaultTypeSelector.setPreferJdkTypes(true); // for (IModule module : system.getModules()) { for (IMovableUnit movableUnit : module.getMovableUnits()) { movableUnitAdded(movableUnit, module); } } // system.registerCacheCallback(this); } /** * {@inheritDoc} */ @Override public List<ITypeSelector> getTypeSelectors() { return _moduleSelectors; } /** * {@inheritDoc} */ @Override public IType getType(String fullyQualifiedName) { return getType(fullyQualifiedName, null); } /** * {@inheritDoc} */ @Override public IType getType(String fullyQualifiedName, IModule referencingModule) { // assert Assert.isNotNull(fullyQualifiedName); // get type modules Set<IType> types = getTypeNameToTypeCache().get(fullyQualifiedName); // return null if type is unknown if (types == null || types.isEmpty()) { return null; } // if multiple type modules exist, throw an exception if (types.size() > 1) { // for (ITypeSelector moduleSelector : _moduleSelectors) { IType type = moduleSelector.selectType(referencingModule, fullyQualifiedName, types); if (type != null) { return type; } } return _defaultTypeSelector.selectType(referencingModule, fullyQualifiedName, types); // throw new AmbiguousElementException(fullyQualifiedName); } // return the type return types.toArray(new IType[0])[0]; } @Override public Set<IType> getTypes() { return Collections.unmodifiableSet(getTypeToModuleCache().keySet()); } /** * {@inheritDoc} */ @Override public Set<IType> getTypes(String fullyQualifiedName) { return getTypes(fullyQualifiedName, null); } @Override public Set<IType> getTypes(String fullyQualifiedName, IModule referencingModule) { // Assert.isNotNull(fullyQualifiedName); Assert.isTrue(fullyQualifiedName.trim().length() > 0); // get type modules Set<IType> types = getTypeNameToTypeCache().get(fullyQualifiedName); types = types != null ? types : new HashSet<IType>(); // return the result return Collections.unmodifiableSet(types); } @Override public Set<IReference> getUnsatisfiedReferences(IModule resourceModule) { // Set<IReference> result = new HashSet<IReference>(); // Set<IReference> references = resourceModule.adaptAs(ITypeModule.class).getReferences(); for (IReference iReference : references) { if (getType(iReference.getFullyQualifiedName(), resourceModule) == null) { result.add(iReference); } } // return result; } /** * <p> * </p> * * @return */ // TODO set to protected public final GenericCache<String, Set<IType>> getTypeNameToTypeCache() { // if (_typeNameToTypeCache == null) { // create _typeNameToTypeCache _typeNameToTypeCache = new GenericCache<String, Set<IType>>() { @Override protected Set<IType> create(String key) { return new HashSet<IType>(); } }; } // return _typeNameToTypeCache; } /** * <p> * </p> * * @return */ public Map<String, Set<IType>> getReferencedTypes() { return Collections.unmodifiableMap(_typeNameToReferringCache); } /** * <p> * </p> * * @return */ // TODO: incremental updates - replace with API public GenericCache<String, Set<IType>> getTypeNameToReferringCache() { // if (_typeNameToReferringCache == null) { // create _typeNameToReferringCache _typeNameToReferringCache = new GenericCache<String, Set<IType>>() { @Override protected Set<IType> create(String key) { return new HashSet<IType>(); } }; } // return _typeNameToReferringCache; } /** * <p> * </p> * * @return */ protected final GenericCache<IType, Set<IModule>> getTypeToModuleCache() { // if (_typeToModuleCache == null) { // create _typeToModuleCache _typeToModuleCache = new GenericCache<IType, Set<IModule>>() { @Override protected Set<IModule> create(IType type) { return new HashSet<IModule>(); } }; } return _typeToModuleCache; } /** * <p> * </p> * * @param type * @return */ public IModule getAssociatedModule(IType type) { // Assert.isNotNull(type); // Set<IModule> modules = _typeToModuleCache.get(type); // if (modules == null || modules.isEmpty()) { return null; } else if (modules.size() > 1) { throw new RuntimeException("Type is contained in multiple modules."); } else { return modules.toArray(new IModule[0])[0]; } } public void internalTypeChanged(IType type, IModule module, ChangeAction action) { switch (action) { case ADDED: { // step 1: type -> module getTypeToModuleCache().getOrCreate(type).add(module); // step 2: type name -> type getTypeNameToTypeCache().getOrCreate(type.getFullyQualifiedName()).add(type); // step 3: referenced type name -> type for (IReference reference : type.getReferences()) { getTypeNameToReferringCache().getOrCreate(reference.getFullyQualifiedName()).add(type); } // ITypeModule typeModule = module.adaptAs(ITypeModule.class); typeModule.add(type); // break; } case REMOVED: { // ITypeModule typeModule = module.adaptAs(ITypeModule.class); typeModule.remove(type); // step 2a: type -> module Set<IModule> typeModules = _typeToModuleCache.get(type); if (typeModules != null) { typeModules.remove(module); if (typeModules.isEmpty()) { _typeToModuleCache.remove(type); // step 2b: type name -> type Set<IType> types = _typeNameToTypeCache.get(type.getFullyQualifiedName()); if (types != null) { // remove the type types.remove(type); // remove types if empty if (types.isEmpty()) { _typeNameToTypeCache.remove(type.getFullyQualifiedName()); } } // step 2c: referenced type name -> type for (IReference reference : type.getReferences()) { Set<IType> referredTypes = _typeNameToReferringCache.get(reference.getFullyQualifiedName()); if (referredTypes != null) { // remove the referred type referredTypes.remove(type); // remove referred types if empty if (referredTypes.isEmpty()) { _typeNameToReferringCache.remove(reference.getFullyQualifiedName()); } } } } } break; } default: { throw new RuntimeException(String.format("Unkown ChangeAction '%s'!", action)); } } } /** * <p> * </p> * * @param types * @param module * @param action */ public void typesChanged(Collection<IType> types, IModule module, ChangeAction action) { // for (IType type : types) { internalTypeChanged(type, module, action); } } /** * <p> * </p> * * @param type * @param module * @param action */ public void typeChanged(IType type, IModule module, ChangeAction action) { // internalTypeChanged(type, module, action); } /** * {@inheritDoc} */ public void clearCaches() { // clear all the caches getTypeNameToTypeCache().clear(); getTypeNameToReferringCache().clear(); getTypeToModuleCache().clear(); } /** * {@inheritDoc} */ @Override public void movableUnitAdded(IMovableUnit movableUnit, IModule module) { for (IProjectContentResource moduleResource : movableUnit.getAssociatedBinaryResources()) { typesChanged(moduleResource.adaptAs(ITypeResource.class).getContainedTypes(), module, ChangeAction.ADDED); } if (movableUnit.hasAssociatedSourceResource()) { typesChanged(movableUnit.getAssociatedSourceResource().adaptAs(ITypeResource.class).getContainedTypes(), module, ChangeAction.ADDED); } } /** * {@inheritDoc} */ @Override public void movableUnitRemoved(IMovableUnit movableUnit, IModule module) { for (IProjectContentResource moduleResource : movableUnit.getAssociatedBinaryResources()) { typesChanged(moduleResource.adaptAs(ITypeResource.class).getContainedTypes(), module, ChangeAction.REMOVED); } if (movableUnit.hasAssociatedSourceResource()) { typesChanged(movableUnit.getAssociatedSourceResource().adaptAs(ITypeResource.class).getContainedTypes(), module, ChangeAction.REMOVED); } } private enum ChangeAction { ADDED, REMOVED; } }