blob: ab4187136a357ac36e7dd0b0be8f987d85b0f4c3 [file] [log] [blame]
/**
* Copyright (c) 2011, 2014 - Lunifera GmbH (Gross Enzersdorf, Austria), Loetz GmbH&Co.KG (69115 Heidelberg, Germany)
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Florian Pirchner - Initial implementation
*
* Based on Xtext org.eclipse.xtext.common.types.access.impl.ClassFinder
*
*/
package org.eclipse.osbp.dsl.xtext.types.bundles;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.osbp.runtime.common.types.IBundleSpace;
import org.eclipse.xtext.common.types.access.impl.ClassNameUtil;
import org.eclipse.xtext.common.types.access.impl.Primitives;
import org.eclipse.xtext.resource.XtextResourceSet;
import org.osgi.framework.Bundle;
import com.google.inject.Inject;
@SuppressWarnings("restriction")
public class BundleSpace implements IBundleSpace {
private static final Class<?> NULL_CLASS;
@Inject
private XtextResourceSet resourceSet;
static {
class Null {
}
NULL_CLASS = Null.class;
}
private final Set<Bundle> bundles = Collections
.synchronizedSet(new HashSet<Bundle>());
private final ClassNameUtil classNameUtil;
private final Class<?> nullValue;
private final Cache cache;
public BundleSpace() {
this.nullValue = NULL_CLASS;
this.cache = new Cache();
this.classNameUtil = new ClassNameUtil();
}
public Set<Bundle> getBundles() {
return new HashSet<>(bundles);
}
/**
* Tries to find a class for the given class name using the bundles
* registered in the bundle space.
*
* @param name
* @return
* @throws ClassNotFoundException
*/
public Class<?> forName(String name) throws ClassNotFoundException {
Class<?> result = null;
synchronized (cache) {
result = cache.get(name);
if (result != null) {
if (result == nullValue) {
throw new ClassNotFoundException();
}
return result;
}
synchronized (bundles) {
for (Bundle bundle : bundles) {
try {
result = bundle.loadClass(classNameUtil
.normalizeClassName(name));
if (result != null) {
break;
}
} catch (ClassNotFoundException e) {
// nothing to do
}
}
}
if (result != null) {
cache.put(name, result);
} else {
cache.put(name, NULL_CLASS);
throw new ClassNotFoundException();
}
}
return result;
}
/**
* Adds a new bundle to the bundle space. The bundle will be used to find
* classes.
*
* @param bundle
*/
public void add(Bundle bundle) {
bundles.add(bundle);
synchronized (cache) {
cache.resetNullValue();
}
}
/**
* Removes the bundle from the bundle space.
*
* @param bundle
*/
public void remove(Bundle bundle) {
bundles.remove(bundle);
}
@SuppressWarnings("serial")
private class Cache extends HashMap<String, Class<?>> {
private static final int INITIAL_SIZE = 500;
public Cache() {
super(INITIAL_SIZE);
for (Class<?> primitiveType : Primitives.ALL_PRIMITIVE_TYPES) {
put(primitiveType.getName(), primitiveType);
}
}
public void resetNullValue() {
for (Iterator<Map.Entry<String, Class<?>>> iterator = entrySet()
.iterator(); iterator.hasNext();) {
Map.Entry<String, Class<?>> entry = iterator.next();
if (entry.getValue() == NULL_CLASS) {
unloadClassResource(entry);
iterator.remove();
}
}
}
/**
* Unloads the class resource.
*
* @param entry
*/
private void unloadClassResource(Map.Entry<String, Class<?>> entry) {
Resource resource = resourceSet.getResource(
URI.createURI(String.format("java://%s/Objects/%s",
entry.getKey(), entry.getKey())), false);
if (resource != null) {
resource.unload();
resourceSet.getResources().remove(resource);
}
}
}
}