blob: 5fd81e005cab3086229f797c3b8c4c659af035d2 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2017 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.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
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.IProgressMonitor;
import org.eclipse.core.runtime.ListenerList;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.OperationCanceledException;
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.ModelException;
import org.eclipse.dltk.core.RuntimePerformanceMonitor;
import org.eclipse.dltk.core.RuntimePerformanceMonitor.PerformanceNode;
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.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 long REQUEST_CACHE_EXPIRE_TIME = 2000;
private static final boolean DEBUG = false;
private static final boolean TRACE = false;
public static final String SEPARATOR = String
.valueOf(IIndexConstants.SEPARATOR);
private static final int CACHE_LIMIT = 250000;
private static final int KEYS_CACHE_LIMIT = 500000;
private static final int REQUEST_CACHE_LIMIT = 500;
private final MixinCache cache;
/**
* Contains map of source modules to mixin elements.
*/
private Map<ISourceModule, List<MixinElement>> elementToMixinCache = new HashMap<>();
private final RequestCache requestCache = new RequestCache(
REQUEST_CACHE_LIMIT);
// true if exists, false if doesn't
private Map<String, Boolean> knownKeysCache = new HashMap<>();
// boolean, for the atomicity of it
public boolean removes = true;
private final IDLTKLanguageToolkit toolkit;
private final IScriptProject project;
private MixinRequestor mixinRequestor = new MixinRequestor();
private ISourceModule currentModule;
/**
* modules required to be reparsed
*/
private Set<ISourceModule> modulesToReparse = new HashSet<>();
/**
* 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(CACHE_LIMIT);
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);
}
// long-running operation
public IMixinElement get(String key) {
if (DLTKCore.VERBOSE) {
System.out.println("MixinModel.get(" + key + ')'); //$NON-NLS-1$
}
MixinElement element = null;
synchronized (this) {
if (knownKeysCache.get(key) == Boolean.FALSE) {
return null;
}
element = (MixinElement) cache.get(key);
if (element == null) {
if (!removes) {
return null;
} else {
element = new MixinElement(key, currentModule);
cache.put(key, element);
cache.ensureSpaceLimit(1, element);
}
}
if (DLTKCore.VERBOSE) {
System.out
.println("Filling ratio:" + this.cache.fillingRatio()); //$NON-NLS-1$
this.cache.printStats();
}
}
buildElementTree(element);
synchronized (this) {
if (element.isFinal() && element.sourceModules.size() > 0) {
knownKeysCache.put(key, Boolean.TRUE);
return element;
}
knownKeysCache.put(key, Boolean.FALSE);
cache.remove(element.key);
cache.resetSpaceLimit(CACHE_LIMIT, element);
}
return null;
}
private IDLTKSearchScope createSearchScope() {
if (project != null) {
return SearchEngine.createSearchScope(project);
} else {
return SearchEngine.createWorkspaceScope(toolkit);
}
}
private static class RequestCacheEntry {
long expireTime;
String prefix = null;
Set<ISourceModule> modules = null;
Set<String> keys = null;
}
private static class RequestCache extends OverflowingLRUCache {
public RequestCache(int size) {
super(size);
}
public RequestCache(int size, int overflow) {
super(size, overflow);
}
@Override
protected boolean close(LRUCacheEntry entry) {
return true;
}
@Override
protected LRUCache newInstance(int size, int overflow) {
return new RequestCache(size, overflow);
}
}
/**
* @deprecated
*/
@Deprecated
public IMixinElement[] find(String pattern, long delta) {
return find(pattern, new NullProgressMonitor());
}
/**
* @since 2.0
*/
public IMixinElement[] find(String pattern, IProgressMonitor monitor) {
// long-running operation
long start = TRACE ? System.currentTimeMillis() : 0;
RequestCacheEntry entry = findFromMixin(pattern, monitor);
if (entry.modules == null || entry.modules.size() == 0) {
return new IMixinElement[0];
}
long parses = TRACE ? System.currentTimeMillis() : 0;
for (ISourceModule module : entry.modules) {
reportModule(module);
}
long parsee = TRACE ? System.currentTimeMillis() : 0;
Set<MixinElement> result = new HashSet<>();
synchronized (this) {
for (String key : entry.keys) {
MixinElement element = getCreateEmpty(key);
if (!monitor.isCanceled()) {
markElementAsFinal(element);
}
addKeyToSet(result, element, pattern);
}
}
if (TRACE) {
long end = System.currentTimeMillis();
System.out.println("MixinModel::find.time:" //$NON-NLS-1$
+ String.valueOf(end - start));
System.out.println("MixinModel::find.parsetime:" //$NON-NLS-1$
+ String.valueOf(parsee - parses));
}
return result.toArray(new IMixinElement[result.size()]);
}
// called with lock being held
private void addKeyToSet(Set<MixinElement> result, MixinElement element,
String pattern) {
// Skip all not matched keys
if (!CharOperation.match(pattern.toCharArray(),
element.key.toCharArray(), true)) {
return;
}
result.add(element);
knownKeysCache.put(element.key, Boolean.TRUE);
for (MixinElement child : element.children)
addKeyToSet(result, child, pattern);
}
// long-running operation
private RequestCacheEntry findFromMixin(String pattern,
IProgressMonitor monitor) {
PerformanceNode p = RuntimePerformanceMonitor.begin();
RequestCacheEntry entry;
synchronized (this) {
entry = (RequestCacheEntry) requestCache.get(pattern);
if (entry != null && entry.expireTime >= System.currentTimeMillis())
return entry;
entry = new RequestCacheEntry();
// TODO searches with clashing keys
// requestCache.put(pattern, entry);
}
Map<ISourceModule, Set<String>> keys = new HashMap<>();
ISourceModule[] containedModules = null;
try {
containedModules = SearchEngine.searchMixinSources(
createSearchScope(), pattern, toolkit, keys, monitor);
} catch (OperationCanceledException e) {
return entry;
}
entry.expireTime = System.currentTimeMillis()
+ REQUEST_CACHE_EXPIRE_TIME;
entry.modules = new HashSet<>(
Arrays.asList(containedModules));
entry.prefix = pattern;
entry.keys = new HashSet<>();
for (Set<String> strs : keys.values()) {
entry.keys.addAll(strs);
}
if (!monitor.isCanceled()) {
synchronized (this) {
requestCache.put(pattern, entry);
}
}
p.done(getNature(), "Mixin model search items", 0);
return entry;
}
/**
* @deprecated
*/
@Deprecated
public IMixinElement[] find(String pattern) {
return find(pattern, new NullProgressMonitor());
}
/**
* @deprecated
*/
@Deprecated
public String[] findKeys(String pattern) {
return findKeys(pattern, new NullProgressMonitor());
}
/**
* @since 2.0
*/
public String[] findKeys(String pattern, IProgressMonitor monitor) {
RequestCacheEntry entry = findFromMixin(pattern, monitor);
return entry.keys.toArray(new String[entry.keys.size()]);
}
// long-running operation
public boolean keyExists(String key) {
synchronized (this) {
// TODO: For this version we cache all information, so should be
// false.
if (!removes) {
return cache.get(key) != null;
}
MixinElement e = (MixinElement) this.cache.get(key);
if (e != null && e.sourceModules.size() > 0) {
return true;
}
Boolean cached = knownKeysCache.get(key);
if (cached != null)
return cached;
}
boolean exists = get(key) != null;
synchronized (this) {
if (knownKeysCache.size() > KEYS_CACHE_LIMIT) {
knownKeysCache.clear();
}
knownKeysCache.put(key, exists);
}
return exists;
}
// long-running operation
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.key);
cache.resetSpaceLimit(CACHE_LIMIT, element);
}
return;
}
for (ISourceModule module : containedModules) {
reportModule(module);
}
// mark selected element and all subelements as finished.
synchronized (this) {
markElementAsFinal(element);
}
}
// called with lock being held
private void markElementAsFinal(MixinElement element) {
element.bFinal = true;
for (MixinElement child : element.children) {
markElementAsFinal(child);
}
}
// TODO long-running operation. shouldn't be synchronized
public synchronized void reportModule(ISourceModule sourceModule) {
if (!elementToMixinCache.containsKey(sourceModule)) {
elementToMixinCache.put(sourceModule,
new ArrayList<MixinElement>());
} else {
// Module already in model. So we do not to rebuild it.
if (!modulesToReparse.remove(sourceModule)) {
return;
}
// We need to reparse module if some elements are moved from it.
}
try {
IMixinParser 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
* @since 2.0
*/
public ISourceModule[] findModules(String key, IProgressMonitor monitor) {
RequestCacheEntry entry = findFromMixin(key, monitor);
return entry.modules.toArray(new ISourceModule[entry.modules.size()]);
}
/**
* @Deprecated
*/
public ISourceModule[] findModules(String key) {
return findModules(key, new NullProgressMonitor());
}
/**
* 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 MixinElement getCreateEmpty(String key) {
// called with lock being held
MixinElement element = (MixinElement) 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() {
@Override
public void elementChanged(ElementChangedEvent event) {
IModelElementDelta delta = event.getDelta();
synchronized (MixinModel.this) {
processDelta(delta);
}
}
// called with lock being held
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;
} else if ((delta.getKind() == IModelElementDelta.REMOVED
|| delta.getKind() == IModelElementDelta.CHANGED)
&& (element.getElementType() == IModelElement.SCRIPT_FOLDER
|| element
.getElementType() == IModelElement.PROJECT_FRAGMENT)) {
if (delta.getAffectedChildren().length == 0) {
try {
element.accept(element1 -> {
if (element1
.getElementType() == ISourceModule.SOURCE_MODULE) {
remove((ISourceModule) element1);
return false;
}
return true;
});
} catch (ModelException e) {
if (DLTKCore.DEBUG) {
e.printStackTrace();
}
}
}
}
if (delta.getKind() == IModelElementDelta.ADDED) {
if (element.getElementType() == IModelElement.SOURCE_MODULE) {
if (modulesToReparse.add((ISourceModule) element)) {
reportModule((ISourceModule) element);
}
}
knownKeysCache.clear();
requestCache.flush();
}
if ((delta.getFlags() & IModelElementDelta.F_CHILDREN) != 0) {
for (IModelElementDelta child : delta.getAffectedChildren()) {
processDelta(child);
}
} else if (delta.getKind() == IModelElementDelta.REMOVED && element
.getElementType() == IModelElement.SCRIPT_FOLDER) {
/* folder delete delta has no children */
MixinModel.this.removeFolder((IScriptFolder) element);
}
}
@Override
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<ISourceModule> toRemove = new ArrayList<>();
synchronized (this) {
IProject project = (IProject) resource;
for (ISourceModule module : elementToMixinCache
.keySet()) {
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 (ISourceModule module : toRemove) {
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$
}
List<MixinElement> mixinCache = elementToMixinCache.get(element);
if (mixinCache != null) {
removeFromRequestCache(element);
for (MixinElement mixin : mixinCache) {
removes = true;
knownKeysCache.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.key);
cache.resetSpaceLimit(CACHE_LIMIT, mixin);
}
}
this.elementToMixinCache.remove(element);
}
}
/**
* @param folder
*/
protected synchronized void removeFolder(IScriptFolder folder) {
final IPath folderPath = folder.getPath();
final List<ISourceModule> modulesToRemove = new ArrayList<>();
for (final ISourceModule module : elementToMixinCache.keySet()) {
final IPath path = module.getPath();
if (folderPath.isPrefixOf(path)) {
modulesToRemove.add(module);
}
}
for (ISourceModule module : modulesToRemove) {
remove(module);
}
}
// called with lock being held
private void removeFromRequestCache(ISourceModule element) {
// Clear requests cache.
@SuppressWarnings("unchecked")
Enumeration<RequestCacheEntry> enumeration = this.requestCache
.elements();
while (enumeration.hasMoreElements()) {
RequestCacheEntry entry = enumeration.nextElement();
if (entry.modules != null) {
if (entry.modules.contains(element)) {
// we can do it now
this.requestCache.remove(entry.prefix);
}
}
}
}
/***************************************************************************
* 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 static final Object[] NO_OBJECTS = new Object[0];
private final class MixinElement
implements IMixinElement, IInternalMixinElement {
private String key;
private boolean bFinal = false;
private List<ISourceModule> sourceModules = new ArrayList<>();
private Map<ISourceModule, List<Object>> sourceModuleToObject = new HashMap<>();
private Set<MixinElement> children = new HashSet<>();
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj instanceof MixinElement) {
return this.key.equals(((MixinElement) obj).key);
}
return false;
}
@Override
public int hashCode() {
return this.key.hashCode();
}
/*
* public MixinElement(String key) { this.key = key; }
*/
@Override
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); }
*/
// called with lock being held
void addInfo(ElementInfo info, ISourceModule module) {
if (info.object != null) {
List<Object> list = this.sourceModuleToObject.get(module);
if (list == null) {
list = new ArrayList<>();
this.sourceModuleToObject.put(module, list);
}
list.add(info.object);
}
}
// called with lock being held
public MixinElement(String key, ISourceModule currentModule) {
this.key = key;
addModule(currentModule);
}
// called with lock being held
void addModule(ISourceModule currentModule) {
if (currentModule != null) {
if (!this.sourceModules.contains(currentModule)) {
this.sourceModules.add(currentModule);
}
}
}
@Override
public IMixinElement[] getChildren() {
this.validate();
synchronized (MixinModel.this) {
return children.toArray(new IMixinElement[children.size()]);
}
}
@Override
public IMixinElement getChildren(String key) {
this.validate();
return MixinModel.this
.get(this.key + IMixinRequestor.MIXIN_NAME_SEPARATOR + key);
}
@Override
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);
}
@Override
public String getLastKeySegment() {
int pos = key.lastIndexOf(IMixinRequestor.MIXIN_NAME_SEPARATOR);
if (pos == -1) {
return key;
}
return key.substring(pos + 1);
}
@Override
public IMixinElement getParent() {
String parentKey = this.getParentKey();
if (parentKey == null) {
return null;
}
return get(parentKey);
}
@Override
public ISourceModule[] getSourceModules() {
this.validate();
// TODO understand why we need this
if (!isFinal()) {
get(key);
}
synchronized (MixinModel.this) {
return this.sourceModules
.toArray(new ISourceModule[this.sourceModules.size()]);
}
}
@Override
public Object[] getObjects(ISourceModule module) {
this.validate();
synchronized (MixinModel.this) {
List<Object> list = this.sourceModuleToObject.get(module);
if (list == null)
return NO_OBJECTS;
Object[] objs = list.toArray();
for (Object obj : objs) {
notifyInitializeListener(this, module, obj);
}
return objs;
}
}
@Override
public Object[] getAllObjects() {
this.validate();
synchronized (MixinModel.this) {
Set<Object> objects = new HashSet<>();
for (ISourceModule module : sourceModules) {
for (Object obj : this.getObjects(module)) {
objects.add(obj);
}
}
return objects.toArray();
}
}
public boolean isFinal() {
return bFinal;
}
@Override
public void close() {
synchronized (MixinModel.this) {
knownKeysCache.remove(key);
removes = true;
this.bFinal = false;
for (int i = 0; i < sourceModules.size(); i++) {
ISourceModule module = sourceModules.get(i);
List<MixinElement> list = elementToMixinCache.get(module);
if (list != null) {
list.remove(this);
if (list.size() == 0) {
elementToMixinCache.remove(module);
}
}
if (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) {
removes = true;
knownKeysCache.remove(parent.key);
parent.children.remove(element);
parent.bFinal = false;
element = parent;
parentKey = parent.getParentKey();
} else {
break;
}
}
}
}
// potentially long-running operation
private void validate() {
if (!isFinal()) {
buildElementTree(this);
}
}
}
private final class MixinRequestor implements IMixinRequestor {
@Override
public void reportElement(ElementInfo info) {
// if( DLTKCore.VERBOSE_MIXIN ) {
// System.out.println("Append mixin:" + info.key);
// }
synchronized (MixinModel.this) {
knownKeysCache.put(info.key, Boolean.TRUE);
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());
parent.children.add(element);
addElementToModules(parent);
element = parent;
}
}
}
}
// called with lock being held
private void addElementToModules(MixinElement element) {
List<MixinElement> elements = MixinModel.this.elementToMixinCache
.get(currentModule);
if (elements == null) {
elements = new ArrayList<>();
MixinModel.this.elementToMixinCache.put(currentModule,
elements);
}
elements.add(element);
}
}
public synchronized void makeAllModuleElementsFinal(ISourceModule module) {
List<MixinElement> elements = elementToMixinCache.get(module);
if (elements != null) {
for (MixinElement mixin : elements) {
removes = true;
mixin.bFinal = true;
}
}
}
public synchronized void makeAllElementsFinalIfNoCacheRemoves() {
if (removes) {
return;
}
Enumeration<?> elements = cache.elements();
while (elements.hasMoreElements()) {
MixinElement e = (MixinElement) elements.nextElement();
e.bFinal = true;
}
}
public void setRemovesToZero() {
removes = false;
}
public synchronized void clearKeysCache(String key) {
knownKeysCache.remove(key);
requestCache.remove(key);
// MixinElement e = (MixinElement)this.cache.get(key);
}
public synchronized void clearKeysCache() {
knownKeysCache.clear();
requestCache.flush();
}
// // Mixin object initialize listeners code
public synchronized void addObjectInitializeListener(
IMixinObjectInitializeListener mixinObjectInitializeListener) {
this.mixinObjectInitializeListeners.add(mixinObjectInitializeListener);
}
public synchronized void removeObjectInitializeListener(
IMixinObjectInitializeListener mixinObjectInitializeListener) {
this.mixinObjectInitializeListeners
.remove(mixinObjectInitializeListener);
}
// called with lock being help
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 synchronized void clear() {
cache.flush();
elementToMixinCache.clear();
knownKeysCache.clear();
modulesToReparse.clear();
requestCache.flush();
}
public String getNature() {
return toolkit.getNatureId();
}
}