/**
*
*/
package org.goko.core.feature;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IContributor;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
import org.eclipse.e4.core.contexts.EclipseContextFactory;
import org.eclipse.e4.core.contexts.IEclipseContext;
import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.core.services.contributions.IContributionFactory;
import org.eclipse.e4.core.services.log.Logger;
import org.eclipse.e4.ui.internal.workbench.E4XMIResource;
import org.eclipse.e4.ui.internal.workbench.ExtensionsSort;
import org.eclipse.e4.ui.internal.workbench.URIHelper;
import org.eclipse.e4.ui.model.application.MApplication;
import org.eclipse.e4.ui.model.application.MApplicationElement;
import org.eclipse.e4.ui.model.fragment.MModelFragment;
import org.eclipse.e4.ui.model.fragment.MModelFragments;
import org.eclipse.e4.ui.model.fragment.impl.FragmentPackageImpl;
import org.eclipse.e4.ui.model.internal.ModelUtils;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.util.EContentsEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
/**
* @author PsyKo
*
*/
public class FeatureSetModelAssembler {
//static final GkLog logger = GkLog.getLogger(DynamicModelFragmentLoader.class);
@Inject
private Logger logger;
@Inject
private MApplication application;
@Inject
private IEclipseContext context;
@Inject
private IExtensionRegistry registry;
/** Extension point id of the feature model fragments */
private static final String EXTENSION_POINT_ID = "org.goko.core.featureset.model";//$NON-NLS-1$
private static final String ATTR_TARGET_BOARD = "targetBoard";//$NON-NLS-1$
// private static final String ALWAYS = "always"; //$NON-NLS-1$
private static final String INITIAL = "initial"; //$NON-NLS-1$
private static final String NOTEXISTS = "notexists"; //$NON-NLS-1$
/**
* Process the model
*/
public void processModel(String targetBoard, boolean initial) {
IExtensionPoint extPoint = registry.getExtensionPoint(EXTENSION_POINT_ID);
IExtension[] extensions = new ExtensionsSort().sort(extPoint.getExtensions());
List<MApplicationElement> imports = new ArrayList<MApplicationElement>();
List<MApplicationElement> addedElements = new ArrayList<MApplicationElement>();
// run processors which are marked to run before fragments
runProcessors(targetBoard, extensions, initial, false);
processFragments(targetBoard, extensions, imports, addedElements, initial);
// run processors which are marked to run after fragments
runProcessors(targetBoard, extensions, initial, true);
resolveImports(imports, addedElements);
}
/**
* @param extensions
* @param imports
* @param addedElements
*/
private void processFragments(String targetBoard, IExtension[] extensions, List<MApplicationElement> imports,
List<MApplicationElement> addedElements, boolean initial) {
for (IExtension extension : extensions) {
IConfigurationElement[] ces = extension.getConfigurationElements();
for (IConfigurationElement ce : ces) {
if ("fragment".equals(ce.getName())) { //$NON-NLS-1$
if(StringUtils.equals(targetBoard, ce.getAttribute(ATTR_TARGET_BOARD))){
if (initial || !INITIAL.equals(ce.getAttribute("apply"))) { //$NON-NLS-1$
processFragment(targetBoard, ce, imports, addedElements, initial);
}
}
}
}
}
}
private void processFragment(String targetBoard,IConfigurationElement ce, List<MApplicationElement> imports, List<MApplicationElement> addedElements, boolean initial) {
E4XMIResource applicationResource = (E4XMIResource) ((EObject) application).eResource();
ResourceSet resourceSet = applicationResource.getResourceSet();
IContributor contributor = ce.getContributor();
String attrURI = ce.getAttribute("uri"); //$NON-NLS-1$
String bundleName = contributor.getName();
if (attrURI == null) {
logger.warn("Unable to find location for the model extension \"{0}\"", bundleName); //$NON-NLS-1$
return;
}
URI uri;
try {
// check if the attrURI is already a platform URI
if (URIHelper.isPlatformURI(attrURI)) {
uri = URI.createURI(attrURI);
} else {
String path = bundleName + '/' + attrURI;
uri = URI.createPlatformPluginURI(path, false);
}
} catch (RuntimeException e) {
logger.warn(e, "Invalid location \"" + attrURI + "\" of model extension \"" + bundleName + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return;
}
String contributorURI = URIHelper.constructPlatformURI(contributor);
Resource resource;
try {
resource = resourceSet.getResource(uri, true);
} catch (RuntimeException e) {
logger.warn(e, "Unable to read model extension from \"" + uri.toString() +"\" of \"" + bundleName + "\""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
return;
}
EList<?> contents = resource.getContents();
if (contents.isEmpty()) {
return;
}
Object extensionRoot = contents.get(0);
if (!(extensionRoot instanceof MModelFragments)) {
logger.warn("Unable to create model extension \"{0}\"", bundleName); //$NON-NLS-1$
return;
}
boolean checkExist = !initial && NOTEXISTS.equals(ce.getAttribute("apply")); //$NON-NLS-1$
MModelFragments fragmentsContainer = (MModelFragments) extensionRoot;
List<MModelFragment> fragments = fragmentsContainer.getFragments();
boolean evalImports = false;
for (MModelFragment fragment : fragments) {
List<MApplicationElement> elements = fragment.getElements();
if (elements.size() == 0) {
continue;
}
for (MApplicationElement el : elements) {
EObject o = (EObject) el;
E4XMIResource r = (E4XMIResource) o.eResource();
if (checkExist && applicationResource.getIDToEObjectMap().containsKey(r.getID(o))) {
continue;
}
applicationResource.setID(o, r.getID(o));
if (contributorURI != null)
el.setContributorURI(contributorURI);
// el.getTags().add(targetBoard);
// System.err.println(" Tagging el+"+el+" with "+targetBoard);
// Remember IDs of subitems
TreeIterator<EObject> treeIt = EcoreUtil.getAllContents(o, true);
while (treeIt.hasNext()) {
EObject eObj = treeIt.next();
r = (E4XMIResource) eObj.eResource();
if (contributorURI != null && (eObj instanceof MApplicationElement)){
MApplicationElement appElement = ((MApplicationElement) eObj);
appElement.setContributorURI(contributorURI);
}
applicationResource.setID(eObj, r.getInternalId(eObj));
}
}
List<MApplicationElement> merged = fragment.merge(application);
for (MApplicationElement mApplicationElement : merged) {
mApplicationElement.getTags().add(targetBoard);
System.err.println(" Tagging +"+mApplicationElement+" with "+targetBoard);
}
if (merged.size() > 0) {
evalImports = true;
addedElements.addAll(merged);
} else {
logger.info("Nothing to merge for \"{0}\"", uri); //$NON-NLS-1$
}
}
if (evalImports) {
List<MApplicationElement> localImports = fragmentsContainer.getImports();
if (localImports != null) {
imports.addAll(localImports);
}
}
}
/**
* @param extensions
* @param afterFragments
*/
private void runProcessors(String targetBoard, IExtension[] extensions, boolean initial, boolean afterFragments) {
for (IExtension extension : extensions) {
IConfigurationElement[] ces = extension.getConfigurationElements();
for (IConfigurationElement ce : ces) {
boolean parseBoolean = Boolean.parseBoolean(ce.getAttribute("beforefragment")); //$NON-NLS-1$
if ("processor".equals(ce.getName()) && afterFragments != parseBoolean) { //$NON-NLS-1$
if(StringUtils.equals(targetBoard, ce.getAttribute(ATTR_TARGET_BOARD))){
if (initial || !INITIAL.equals(ce.getAttribute("apply"))) { //$NON-NLS-1$
runProcessor(ce);
}
}
}
}
}
}
private void runProcessor(IConfigurationElement ce) {
IEclipseContext localContext = EclipseContextFactory.create();
IContributionFactory factory = context.get(IContributionFactory.class);
for (IConfigurationElement ceEl : ce.getChildren("element")) { //$NON-NLS-1$
String id = ceEl.getAttribute("id"); //$NON-NLS-1$
if (id == null) {
logger.warn("No element id given"); //$NON-NLS-1$
continue;
}
String key = ceEl.getAttribute("contextKey"); //$NON-NLS-1$
if (key == null) {
key = id;
}
MApplicationElement el = ModelUtils.findElementById(application, id);
if (el == null) {
logger.warn("Could not find element with id '" + id + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}
localContext.set(key, el);
}
try {
Object o = factory
.create("bundleclass://" + ce.getContributor().getName() + "/" + ce.getAttribute("class"), //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
context, localContext);
if (o == null) {
logger.warn("Unable to create processor " + ce.getAttribute("class") + " from " + ce.getContributor().getName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} else {
ContextInjectionFactory.invoke(o, Execute.class, context, localContext);
}
} catch (Exception e) {
logger.warn(e, "Could not run processor"); //$NON-NLS-1$
}
}
private void resolveImports(List<MApplicationElement> imports,
List<MApplicationElement> addedElements) {
if (imports.isEmpty())
return;
// now that we have all components loaded, resolve imports
Map<MApplicationElement, MApplicationElement> importMaps = new HashMap<MApplicationElement, MApplicationElement>();
for (MApplicationElement importedElement : imports) {
MApplicationElement realElement = ModelUtils.findElementById(application,
importedElement.getElementId());
if (realElement == null) {
logger.warn("Could not resolve an import element for '" + realElement + "'"); //$NON-NLS-1$ //$NON-NLS-2$
}
importMaps.put(importedElement, realElement);
}
TreeIterator<EObject> it = EcoreUtil.getAllContents(addedElements);
List<Runnable> commands = new ArrayList<Runnable>();
while (it.hasNext()) {
EObject o = it.next();
EContentsEList.FeatureIterator<EObject> featureIterator = (EContentsEList.FeatureIterator<EObject>) o
.eCrossReferences().iterator();
while (featureIterator.hasNext()) {
EObject importObject = featureIterator.next();
if (importObject.eContainmentFeature() == FragmentPackageImpl.Literals.MODEL_FRAGMENTS__IMPORTS) {
EStructuralFeature feature = featureIterator.feature();
MApplicationElement el = importMaps.get(importObject);
if (el == null) {
logger.warn("Could not resolve import for " + el); //$NON-NLS-1$
}
final EObject interalTarget = o;
final EStructuralFeature internalFeature = feature;
final MApplicationElement internalElment = el;
final EObject internalImportObject = importObject;
commands.add(new Runnable() {
@Override
public void run() {
if (internalFeature.isMany()) {
logger.error("Replacing"); //$NON-NLS-1$
@SuppressWarnings("unchecked")
List<Object> l = (List<Object>) interalTarget.eGet(internalFeature);
int index = l.indexOf(internalImportObject);
if (index >= 0) {
l.set(index, internalElment);
}
} else {
interalTarget.eSet(internalFeature, internalElment);
}
}
});
}
}
}
for (Runnable cmd : commands) {
cmd.run();
}
}
}