| /******************************************************************************* |
| * Copyright (c) 2005, 2007 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| |
| *******************************************************************************/ |
| package org.eclipse.dltk.core.mixin; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.IResourceChangeListener; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.ListenerList; |
| import org.eclipse.dltk.compiler.CharOperation; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.DLTKLanguageManager; |
| import org.eclipse.dltk.core.ElementChangedEvent; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IElementChangedListener; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IModelElementDelta; |
| import org.eclipse.dltk.core.IScriptFolder; |
| import org.eclipse.dltk.core.IScriptProject; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.mixin.IMixinRequestor.ElementInfo; |
| import org.eclipse.dltk.core.search.IDLTKSearchScope; |
| import org.eclipse.dltk.core.search.SearchEngine; |
| import org.eclipse.dltk.core.search.indexing.IIndexConstants; |
| import org.eclipse.dltk.internal.core.ModelCache; |
| import org.eclipse.dltk.internal.core.OverflowingLRUCache; |
| import org.eclipse.dltk.internal.core.mixin.IInternalMixinElement; |
| import org.eclipse.dltk.internal.core.mixin.MixinCache; |
| import org.eclipse.dltk.internal.core.mixin.MixinManager; |
| import org.eclipse.dltk.internal.core.util.LRUCache; |
| |
| public class MixinModel { |
| private static final boolean DEBUG = false; |
| private static final boolean TRACE = false; |
| |
| public static final String SEPARATOR = String |
| .valueOf(IIndexConstants.SEPARATOR); |
| |
| private final MixinCache cache; |
| |
| /** |
| * Contains map of source modules to mixin elements. |
| */ |
| private Map elementToMixinCache = new HashMap(); |
| private final IDLTKLanguageToolkit toolkit; |
| |
| private final IScriptProject project; |
| private IDLTKSearchScope projectScope = null; |
| |
| private MixinRequestor mixinRequestor = new MixinRequestor(); |
| |
| private ISourceModule currentModule; |
| |
| /** |
| * modules required to be reparsed |
| */ |
| private Set modulesToReparse = new HashSet(); |
| public long removes = 1; |
| private final double ratio = 50000; |
| |
| /** |
| * Creates workspace instance |
| * |
| * @param toolkit |
| */ |
| public MixinModel(IDLTKLanguageToolkit toolkit) { |
| this(toolkit, null); |
| } |
| |
| /** |
| * Creates project instance |
| * |
| * @param toolkit |
| * @param project |
| */ |
| public MixinModel(IDLTKLanguageToolkit toolkit, IScriptProject project) { |
| this.toolkit = toolkit; |
| this.project = project; |
| |
| // long maxMemory = Runtime.getRuntime().freeMemory(); |
| |
| this.cache = new MixinCache( |
| (int) (ModelCache.DEFAULT_ROOT_SIZE * ratio)); |
| DLTKCore.addElementChangedListener(changedListener, |
| ElementChangedEvent.POST_CHANGE); |
| ResourcesPlugin.getWorkspace().addResourceChangeListener( |
| changedListener); |
| MixinModelRegistry.register(this); |
| } |
| |
| public void stop() { |
| DLTKCore.removeElementChangedListener(changedListener); |
| ResourcesPlugin.getWorkspace().removeResourceChangeListener( |
| changedListener); |
| MixinModelRegistry.unregister(this); |
| } |
| |
| public IMixinElement get(String key) { |
| if (DLTKCore.VERBOSE) { |
| System.out.println("MixinModel.get(" + key + ')'); //$NON-NLS-1$ |
| } |
| if (notExistKeysCache.contains(key)) { |
| return null; |
| } |
| if (removes == 0) { |
| if (cache.get(key) == null) { |
| return null; |
| } |
| } |
| MixinElement element = getCreateEmpty(key); |
| if (DLTKCore.VERBOSE) { |
| System.out.println("Filling ratio:" + this.cache.fillingRatio()); //$NON-NLS-1$ |
| this.cache.printStats(); |
| } |
| buildElementTree(element); |
| if (element.isFinal() && element.sourceModules.size() > 0) { |
| existKeysCache.add(key); |
| return element; |
| } |
| notExistKeysCache.add(key); |
| synchronized (this.cache) { |
| this.cache.remove(element); |
| cache.resetSpaceLimit(ModelCache.DEFAULT_ROOT_SIZE, element); |
| this.cache.removeKey(element.key); |
| } |
| return null; |
| } |
| |
| private IDLTKSearchScope createSearchScope() { |
| if (project != null) { |
| if (projectScope == null) { |
| projectScope = SearchEngine.createSearchScope(project); |
| } |
| return projectScope; |
| } else { |
| return SearchEngine.createWorkspaceScope(toolkit); |
| } |
| } |
| |
| private static class RequestCacheEntry { |
| String prefix = null; |
| Set modules = null; |
| Set keys = null; |
| } |
| |
| private static class RequestCache extends OverflowingLRUCache { |
| |
| public RequestCache(int size) { |
| super(size); |
| } |
| |
| public RequestCache(int size, int overflow) { |
| super(size, overflow); |
| } |
| |
| protected boolean close(LRUCacheEntry entry) { |
| return true; |
| } |
| |
| protected LRUCache newInstance(int size, int overflow) { |
| return new RequestCache(size, overflow); |
| } |
| }; |
| |
| private final RequestCache requestCache = new RequestCache(500); |
| |
| public IMixinElement[] find(String pattern, long delta) { |
| long start = TRACE ? System.currentTimeMillis() : 0; |
| |
| RequestCacheEntry entry = findFromMixin(pattern); |
| |
| if (entry.modules == null || entry.modules.size() == 0) { |
| return new IMixinElement[0]; |
| } |
| |
| long parses = TRACE ? System.currentTimeMillis() : 0; |
| for (Iterator iterator = entry.modules.iterator(); iterator.hasNext();) { |
| ISourceModule module = (ISourceModule) iterator.next(); |
| reportModule(module); |
| } |
| long parsee = TRACE ? System.currentTimeMillis() : 0; |
| |
| Set result = new HashSet(); |
| |
| // int i = 0; |
| for (Iterator iterator = entry.keys.iterator(); iterator.hasNext();) { |
| MixinElement element = getCreateEmpty((String) iterator.next()); |
| markElementAsFinal(element); |
| addKeyToSet(result, element, pattern); |
| } |
| if (TRACE) { |
| long end = System.currentTimeMillis(); |
| System.out.println("MixinModel::find.time:" |
| + String.valueOf(end - start)); |
| System.out.println("MixinModel::find.parsetime:" |
| + String.valueOf(parsee - parses)); |
| } |
| |
| return (IMixinElement[]) result |
| .toArray(new IMixinElement[result.size()]); |
| } |
| |
| private void addKeyToSet(Set result, MixinElement element, String pattern) { |
| // Skip all not matched keys |
| if (!CharOperation.match(pattern.toCharArray(), element.key |
| .toCharArray(), true)) { |
| return; |
| } |
| result.add(element); |
| existKeysCache.add(element.key); |
| notExistKeysCache.remove(element.key); |
| IMixinElement[] children = (IMixinElement[]) element.children |
| .toArray(new IMixinElement[element.children.size()]); |
| for (int i = 0; i < children.length; i++) { |
| addKeyToSet(result, (MixinElement) children[i], pattern); |
| } |
| } |
| |
| private RequestCacheEntry findFromMixin(String pattern) { |
| RequestCacheEntry entry = (RequestCacheEntry) requestCache.get(pattern); |
| // Set modules = new HashSet(); |
| if (entry == null) { |
| Map keys = new HashMap(); |
| ISourceModule[] containedModules = SearchEngine.searchMixinSources( |
| createSearchScope(), pattern, toolkit, keys); |
| entry = new RequestCacheEntry(); |
| entry.modules = new HashSet(Arrays.asList(containedModules)); |
| entry.prefix = pattern; |
| Collection values = keys.values(); |
| entry.keys = new HashSet(); |
| for (Iterator iterator = values.iterator(); iterator.hasNext();) { |
| Set vals = (Set) iterator.next(); |
| entry.keys.addAll(vals); |
| } |
| this.requestCache.put(pattern, entry); |
| } |
| return entry; |
| } |
| |
| public IMixinElement[] find(String pattern) { |
| return find(pattern, -1); |
| } |
| |
| public String[] findKeys(String pattern) { |
| RequestCacheEntry entry = findFromMixin(pattern); |
| return (String[]) entry.keys.toArray(new String[entry.keys.size()]); |
| } |
| |
| private Set existKeysCache = new HashSet(); |
| private Set notExistKeysCache = new HashSet(); |
| |
| public boolean keyExists(String key) { |
| // TODO: For this version we cache all information, so should be 0. |
| if (removes == 0) { |
| return this.cache.get(key) != null; |
| } |
| MixinElement e = (MixinElement) this.cache.get(key); |
| if (e != null && e.sourceModules.size() > 0) { |
| return true; |
| } |
| if (existKeysCache.contains(key)) { |
| return true; |
| } |
| if (notExistKeysCache.contains(key)) { |
| return false; |
| } |
| boolean exist = get(key) != null; |
| // System.out.println("1"); |
| if (exist) { |
| if (existKeysCache.size() > 500000) { |
| existKeysCache.clear(); |
| } |
| existKeysCache.add(key); |
| } else { |
| if (notExistKeysCache.size() > 500000) { |
| notExistKeysCache.clear(); |
| } |
| notExistKeysCache.add(key); |
| } |
| |
| return exist; |
| } |
| |
| private void buildElementTree(MixinElement element) { |
| // TODO: This is consistent cache stage |
| if (element.isFinal()) { |
| return; |
| } |
| ISourceModule[] containedModules = findModules(element.getKey()); |
| if (containedModules.length == 0) { |
| synchronized (cache) { |
| cache.remove(element); |
| cache.resetSpaceLimit(ModelCache.DEFAULT_ROOT_SIZE, element); |
| cache.removeKey(element.key); |
| } |
| return; |
| } |
| for (int i = 0; i < containedModules.length; ++i) { |
| reportModule(containedModules[i]); |
| } |
| // mark selected element and all subelements as finished. |
| markElementAsFinal(element); |
| } |
| |
| private synchronized void markElementAsFinal(MixinElement element) { |
| element.bFinal = true; |
| for (Iterator i = element.children.iterator(); i.hasNext();) { |
| markElementAsFinal((MixinElement) i.next()); |
| } |
| } |
| |
| public synchronized void reportModule(ISourceModule sourceModule) { |
| if (!this.elementToMixinCache.containsKey(sourceModule)) { |
| this.elementToMixinCache.put(sourceModule, new ArrayList()); |
| } else { // Module already in model. So we do not to rebuild it. |
| if (!this.modulesToReparse.remove(sourceModule)) { |
| return; |
| } |
| // We need to reparse module if some elements are moved from it. |
| } |
| IMixinParser mixinParser; |
| try { |
| mixinParser = MixinManager.getMixinParser(sourceModule); |
| if (mixinParser != null) { |
| this.currentModule = sourceModule; |
| mixinParser.setRequirestor(mixinRequestor); |
| mixinParser.parserSourceModule(true, sourceModule); |
| this.currentModule = null; |
| } |
| } catch (CoreException e) { |
| DLTKCore.error("Error in reportModule", e); //$NON-NLS-1$ |
| return; |
| } |
| } |
| |
| /** |
| * Should find all elements source modules to be sure we build complete |
| * child tree. |
| * |
| * @param element |
| * @return |
| */ |
| public ISourceModule[] findModules(String key) { |
| RequestCacheEntry entry = findFromMixin(key); |
| return (ISourceModule[]) entry.modules |
| .toArray(new ISourceModule[entry.modules.size()]); |
| } |
| |
| /** |
| * Returns a mixin element from this.cache by it's key, or creates new one |
| * if cache doesn't contain required element |
| * |
| * @param key |
| * @return |
| */ |
| private synchronized MixinElement getCreateEmpty(String key) { |
| MixinElement element = (MixinElement) MixinModel.this.cache.get(key); |
| if (element == null) { |
| element = new MixinElement(key, currentModule); |
| this.cache.put(key, element); |
| this.cache.ensureSpaceLimit(1, element); |
| } |
| return element; |
| } |
| |
| private interface IMixinChangedListener extends IElementChangedListener, |
| IResourceChangeListener { |
| } |
| |
| private IMixinChangedListener changedListener = new IMixinChangedListener() { |
| public void elementChanged(ElementChangedEvent event) { |
| IModelElementDelta delta = event.getDelta(); |
| processDelta(delta); |
| } |
| |
| private void processDelta(IModelElementDelta delta) { |
| IModelElement element = delta.getElement(); |
| if (delta.getKind() == IModelElementDelta.REMOVED |
| || delta.getKind() == IModelElementDelta.CHANGED |
| || (delta.getFlags() & IModelElementDelta.F_REMOVED_FROM_BUILDPATH) != 0 |
| || (delta.getFlags() & IModelElementDelta.CHANGED) != 0) { |
| if (element.getElementType() != IModelElement.SOURCE_MODULE |
| && element.getElementType() != IModelElement.PROJECT_FRAGMENT |
| && element.getElementType() != IModelElement.SCRIPT_FOLDER |
| && element.getElementType() != IModelElement.SCRIPT_MODEL |
| && element.getElementType() != IModelElement.SCRIPT_PROJECT) { |
| ISourceModule module = (ISourceModule) element |
| .getAncestor(IModelElement.SOURCE_MODULE); |
| MixinModel.this.remove(module); |
| } |
| if (element.getElementType() == IModelElement.SOURCE_MODULE) { |
| MixinModel.this.remove((ISourceModule) element); |
| } |
| } |
| |
| if (element.getElementType() == IModelElement.SCRIPT_PROJECT |
| && delta.getKind() == IModelElementDelta.CHANGED |
| && (delta.getFlags() & IModelElementDelta.F_BUILDPATH_CHANGED) != 0) { |
| clear(); |
| return; |
| } |
| if (delta.getKind() == IModelElementDelta.ADDED) { |
| if (element.getElementType() == IModelElement.SOURCE_MODULE) { |
| if (modulesToReparse.add(element)) { |
| reportModule((ISourceModule) element); |
| } |
| } |
| MixinModel.this.notExistKeysCache.clear(); |
| requestCache.flush(); |
| } |
| |
| if ((delta.getFlags() & IModelElementDelta.F_CHILDREN) != 0) { |
| IModelElementDelta[] affectedChildren = delta |
| .getAffectedChildren(); |
| for (int i = 0; i < affectedChildren.length; i++) { |
| IModelElementDelta child = affectedChildren[i]; |
| processDelta(child); |
| } |
| } else if (delta.getKind() == IModelElementDelta.REMOVED |
| && element.getElementType() == IModelElement.SCRIPT_FOLDER) { |
| /* folder delete delta has no children */ |
| MixinModel.this.removeFolder((IScriptFolder) element); |
| } |
| } |
| |
| public void resourceChanged(IResourceChangeEvent event) { |
| int eventType = event.getType(); |
| IResource resource = event.getResource(); |
| // IResourceDelta delta = event.getDelta(); |
| |
| switch (eventType) { |
| case IResourceChangeEvent.PRE_CLOSE: |
| if (resource.getType() == IResource.PROJECT |
| && DLTKLanguageManager |
| .hasScriptNature((IProject) resource)) { |
| if (project != null |
| && resource.equals(project.getProject())) { |
| clear(); |
| // TODO destroy this model |
| return; |
| } |
| } |
| break; |
| case IResourceChangeEvent.PRE_DELETE: |
| if (resource.getType() == IResource.PROJECT |
| && DLTKLanguageManager |
| .hasScriptNature((IProject) resource)) { |
| if (project != null |
| && resource.equals(project.getProject())) { |
| clear(); |
| // TODO destroy this model |
| return; |
| } |
| // remove all resources with given project from model. |
| List toRemove = new ArrayList(); |
| synchronized (elementToMixinCache) { |
| IProject project = (IProject) resource; |
| for (Iterator iterator = elementToMixinCache.keySet() |
| .iterator(); iterator.hasNext();) { |
| ISourceModule module = (ISourceModule) iterator |
| .next(); |
| IScriptProject scriptProject = module |
| .getScriptProject(); |
| if (scriptProject != null) { |
| IProject prj = scriptProject.getProject(); |
| if ((prj != null && prj.equals(project)) |
| || prj == null) { |
| toRemove.add(module); |
| } |
| } else { |
| toRemove.add(module); |
| } |
| } |
| } |
| for (Iterator iterator = toRemove.iterator(); iterator |
| .hasNext();) { |
| ISourceModule module = (ISourceModule) iterator.next(); |
| remove(module); |
| } |
| } |
| return; |
| } |
| } |
| }; |
| |
| // private synchronized void clearAllElementsState() { |
| // Enumeration elements = cache.elements(); |
| // while( elements.hasMoreElements() ) { |
| // MixinElement o = (MixinElement)elements.nextElement(); |
| // o.bFinal = false; |
| // } |
| // } |
| |
| private final String getLogContext() { |
| if (project == null) { |
| return "[MixinModel|$" + toolkit.getLanguageName() + "$]"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } else { |
| return "[MixinModel|" + project.getElementName() + "]"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| |
| private final void log(String message) { |
| System.out.println(getLogContext() + " " + message); //$NON-NLS-1$ |
| } |
| |
| public synchronized void remove(ISourceModule element) { |
| if (DEBUG) { |
| log("remove " + element.getElementName()); //$NON-NLS-1$ |
| } |
| if (this.elementToMixinCache.containsKey(element)) { |
| removeFromRequestCache(element); |
| |
| List elements = (List) this.elementToMixinCache.get(element); |
| for (int i = 0; i < elements.size(); ++i) { |
| removes++; |
| MixinElement mixin = (MixinElement) elements.get(i); |
| existKeysCache.remove(mixin.key); |
| notExistKeysCache.remove(mixin.key); |
| mixin.bFinal = false; |
| mixin.sourceModules.remove(element); |
| mixin.sourceModuleToObject.remove(element); |
| if (mixin.sourceModules.size() == 0) { |
| // Remove frob parent. |
| String parentKey = mixin.getParentKey(); |
| if (parentKey != null) { |
| MixinElement parent = (MixinElement) this.cache |
| .get(parentKey); |
| if (parent != null) { |
| parent.children.remove(mixin); |
| parent.bFinal = false; |
| } |
| } |
| // Remove from cache |
| cache.remove(mixin); |
| cache.resetSpaceLimit(ModelCache.DEFAULT_ROOT_SIZE, mixin); |
| cache.removeKey(mixin.key); |
| } |
| } |
| this.elementToMixinCache.remove(element); |
| } |
| } |
| |
| /** |
| * @param folder |
| */ |
| protected void removeFolder(IScriptFolder folder) { |
| final IPath folderPath = folder.getPath(); |
| final List modulesToRemove = new ArrayList(); |
| for (Iterator i = elementToMixinCache.keySet().iterator(); i.hasNext();) { |
| final ISourceModule module = (ISourceModule) i.next(); |
| final IPath path = module.getPath(); |
| if (folderPath.isPrefixOf(path)) { |
| modulesToRemove.add(module); |
| } |
| } |
| for (Iterator i = modulesToRemove.iterator(); i.hasNext();) { |
| remove((ISourceModule) i.next()); |
| } |
| } |
| |
| private void removeFromRequestCache(ISourceModule element) { |
| // Clear requests cache. |
| List keysToRemove = new ArrayList(); |
| Enumeration enumeration = this.requestCache.elements(); |
| while (enumeration.hasMoreElements()) { |
| RequestCacheEntry entry = (RequestCacheEntry) enumeration |
| .nextElement(); |
| if (entry.modules != null) { |
| if (entry.modules.contains(element)) { |
| keysToRemove.add(entry.prefix); |
| } |
| } |
| } |
| for (Iterator iterator = keysToRemove.iterator(); iterator.hasNext();) { |
| String key = (String) iterator.next(); |
| this.requestCache.remove(key); |
| } |
| } |
| |
| /*************************************************************************** |
| * Then getObjects are called, special initialize listener are called. |
| * |
| */ |
| public interface IMixinObjectInitializeListener { |
| void initialize(IMixinElement element, Object object, |
| ISourceModule module); |
| } |
| |
| private final ListenerList mixinObjectInitializeListeners = new ListenerList(); |
| |
| private class MixinElement implements IMixinElement, IInternalMixinElement { |
| private String key; |
| private boolean bFinal = false; |
| private List sourceModules = new ArrayList(); |
| private Map sourceModuleToObject = new HashMap(); |
| |
| /** |
| * List of Strings. |
| */ |
| private Set children = new HashSet(); |
| |
| public boolean equals(Object obj) { |
| if (this == obj) |
| return true; |
| if (obj instanceof MixinElement) { |
| return this.key.equals(((MixinElement) obj).key); |
| } |
| return false; |
| } |
| |
| public int hashCode() { |
| return this.key.hashCode(); |
| } |
| |
| public MixinElement(String key) { |
| this.key = key; |
| } |
| |
| public String toString() { |
| return this.getLastKeySegment() + " final[" + this.bFinal + "]" //$NON-NLS-1$ //$NON-NLS-2$ |
| + this.children + " "; //$NON-NLS-1$ |
| } |
| |
| public MixinElement(ElementInfo info, ISourceModule module) { |
| this(info.key, currentModule); |
| addInfo(info, module); |
| } |
| |
| void addInfo(ElementInfo info, ISourceModule module) { |
| if (info.object != null) { |
| final Object object = this.sourceModuleToObject.get(module); |
| if (object != null) { |
| if (object instanceof List) { |
| ((List) object).add(info.object); |
| } else { |
| List list = new ArrayList(); |
| list.add(object); |
| list.add(info.object); |
| this.sourceModuleToObject.put(module, list); |
| } |
| } else { |
| List list = new ArrayList(); |
| list.add(info.object); |
| this.sourceModuleToObject.put(module, list); |
| } |
| } |
| } |
| |
| public MixinElement(String key, ISourceModule currentModule) { |
| this.key = key; |
| addModule(currentModule); |
| } |
| |
| void addModule(ISourceModule currentModule) { |
| if (currentModule != null) { |
| if (!this.sourceModules.contains(currentModule)) { |
| this.sourceModules.add(currentModule); |
| } |
| } |
| } |
| |
| public IMixinElement[] getChildren() { |
| this.validate(); |
| return (IMixinElement[]) this.children |
| .toArray(new IMixinElement[this.children.size()]); |
| } |
| |
| public IMixinElement getChildren(String key) { |
| this.validate(); |
| return MixinModel.this.get(this.key |
| + IMixinRequestor.MIXIN_NAME_SEPARATOR + key); |
| } |
| |
| public String getKey() { |
| return this.key; |
| } |
| |
| protected String getParentKey() { |
| int pos = key.lastIndexOf(IMixinRequestor.MIXIN_NAME_SEPARATOR); |
| if (pos == -1) { |
| return null; |
| } |
| return key.substring(0, pos); |
| } |
| |
| public String getLastKeySegment() { |
| int pos = key.lastIndexOf(IMixinRequestor.MIXIN_NAME_SEPARATOR); |
| if (pos == -1) { |
| return key; |
| } |
| return key.substring(pos + 1); |
| } |
| |
| public IMixinElement getParent() { |
| String parentKey = this.getParentKey(); |
| if (parentKey == null) { |
| return null; |
| } |
| return get(parentKey); |
| |
| } |
| |
| public ISourceModule[] getSourceModules() { |
| this.validate(); |
| if (!this.isFinal()) { |
| get(this.key); |
| } |
| return (ISourceModule[]) this.sourceModules |
| .toArray(new ISourceModule[this.sourceModules.size()]); |
| } |
| |
| public Object[] getObjects(ISourceModule module) { |
| this.validate(); |
| Object o = this.sourceModuleToObject.get(module); |
| if (o instanceof List) { |
| |
| Object[] objs = ((List) o).toArray(); |
| for (int i = 0; i < objs.length; i++) { |
| notifyInitializeListener(this, module, objs[i]); |
| } |
| return objs; |
| } |
| if (o != null) { |
| notifyInitializeListener(this, module, o); |
| return new Object[] { o }; |
| } |
| return new Object[0]; |
| } |
| |
| public Object[] getAllObjects() { |
| this.validate(); |
| HashSet objects = new HashSet(); |
| for (Iterator iterator = this.sourceModules.iterator(); iterator |
| .hasNext();) { |
| ISourceModule module = (ISourceModule) iterator.next(); |
| Object[] objs = this.getObjects(module); |
| for (int j = 0; j < objs.length; ++j) { |
| objects.add(objs[j]); |
| } |
| } |
| return objects.toArray(); |
| } |
| |
| public boolean isFinal() { |
| return this.bFinal; |
| } |
| |
| public void close() { |
| existKeysCache.remove(key); |
| notExistKeysCache.remove(key); |
| removes++; |
| this.bFinal = false; |
| for (int i = 0; i < sourceModules.size(); i++) { |
| Object module = sourceModules.get(i); |
| List list = (List) elementToMixinCache.get(module); |
| if (list != null) { |
| list.remove(this); |
| if (list.size() == 0) { |
| elementToMixinCache.remove(module); |
| } |
| } |
| if (!modulesToReparse.contains(module) |
| && elementToMixinCache.containsKey(module)) { |
| modulesToReparse.add(module); |
| } |
| } |
| this.sourceModules.clear(); |
| this.sourceModuleToObject.clear(); |
| |
| // Lets also clean parent data |
| // Remove frob parent. |
| String parentKey = getParentKey(); |
| MixinElement element = this; |
| while (parentKey != null) { |
| MixinElement parent = (MixinElement) cache.get(parentKey); |
| if (parent != null) { |
| existKeysCache.remove(parent.key); |
| notExistKeysCache.remove(parent.key); |
| removes++; |
| parent.children.remove(element); |
| parent.bFinal = false; |
| element = parent; |
| parentKey = parent.getParentKey(); |
| } else { |
| break; |
| } |
| } |
| |
| } |
| |
| private void validate() { |
| if (!isFinal()) { |
| buildElementTree(this); |
| } |
| } |
| }; |
| |
| private class MixinRequestor implements IMixinRequestor { |
| public void reportElement(ElementInfo info) { |
| // if( DLTKCore.VERBOSE_MIXIN ) { |
| // System.out.println("Append mixin:" + info.key); |
| // } |
| existKeysCache.add(info.key); |
| notExistKeysCache.remove(info.key); |
| String[] list = info.key.split("\\" //$NON-NLS-1$ |
| + IMixinRequestor.MIXIN_NAME_SEPARATOR); |
| MixinElement element = getCreateEmpty(info.key); |
| addElementToModules(element); |
| element.addModule(currentModule); |
| element.addInfo(info, currentModule); |
| // Append as childs for all other elements. Also append modules to |
| // all selected elements. |
| if (list.length != 1) { |
| for (int i = 0; i < list.length - 1; ++i) { |
| MixinElement parent = getCreateEmpty(element.getParentKey()); |
| if (!parent.children.contains(element)) { |
| parent.children.add(element); |
| } |
| addElementToModules(parent); |
| element = parent; |
| } |
| } |
| } |
| |
| private void addElementToModules(MixinElement element) { |
| List elements = (List) MixinModel.this.elementToMixinCache |
| .get(currentModule); |
| if (elements == null) { |
| elements = new ArrayList(); |
| MixinModel.this.elementToMixinCache |
| .put(currentModule, elements); |
| } |
| elements.add(element); |
| } |
| } |
| |
| public void makeAllModuleElementsFinal(ISourceModule module) { |
| if (this.elementToMixinCache.containsKey(module)) { |
| List elements = (List) this.elementToMixinCache.get(module); |
| for (int i = 0; i < elements.size(); ++i) { |
| removes++; |
| MixinElement mixin = (MixinElement) elements.get(i); |
| mixin.bFinal = true; |
| } |
| } |
| } |
| |
| public void makeAllElementsFinalIfNoCacheRemoves() { |
| if (removes != 0) { |
| return; |
| } |
| Enumeration elements = cache.elements(); |
| while (elements.hasMoreElements()) { |
| MixinElement e = (MixinElement) elements.nextElement(); |
| e.bFinal = true; |
| } |
| |
| } |
| |
| public void setRemovesToZero() { |
| removes = 0; |
| } |
| |
| public void clearKeysCache(String key) { |
| existKeysCache.remove(key); |
| notExistKeysCache.remove(key); |
| requestCache.remove(key); |
| // MixinElement e = (MixinElement)this.cache.get(key); |
| } |
| |
| // // Mixin object initialize listeners code |
| public void addObjectInitializeListener( |
| IMixinObjectInitializeListener mixinObjectInitializeListener) { |
| this.mixinObjectInitializeListeners.add(mixinObjectInitializeListener); |
| } |
| |
| public void removeObjectInitializeListener( |
| IMixinObjectInitializeListener mixinObjectInitializeListener) { |
| this.mixinObjectInitializeListeners |
| .remove(mixinObjectInitializeListener); |
| } |
| |
| private void notifyInitializeListener(IMixinElement element, |
| ISourceModule module, Object o) { |
| Object[] listeners = mixinObjectInitializeListeners.getListeners(); |
| for (int i = 0; i < listeners.length; i++) { |
| ((IMixinObjectInitializeListener) (listeners[i])).initialize( |
| element, o, module); |
| } |
| } |
| |
| protected void clear() { |
| projectScope = null; |
| cache.flush(); |
| elementToMixinCache.clear(); |
| existKeysCache.clear(); |
| notExistKeysCache.clear(); |
| modulesToReparse.clear(); |
| requestCache.flush(); |
| } |
| |
| public String getNature() { |
| return toolkit.getNatureId(); |
| } |
| } |