blob: 69766c6e46d390d78e7829574e4ac61a4fee3292 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2014 BestSolution.at 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
*
* Contributors:
* Tom Schindl<tom.schindl@bestsolution.at> - initial API and implementation
* Lars Vogel <Lars.Vogel@gmail.com> - Bug 430075, 430080, 431464, 433336
* René Brandstetter - Bug 419749 - [Workbench] [e4 Workbench] - Remove the deprecated PackageAdmin
* Brian de Alwis (MTI) - Bug 433053
* Florian Pirchner - adjusted for Vaaclipse perspectives restore - using different ModelUtils
******************************************************************************/
package org.eclipse.osbp.vaaclipse.addons.common.resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.inject.Inject;
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.MStringModelFragment;
import org.eclipse.e4.ui.model.fragment.impl.FragmentPackageImpl;
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;
import org.eclipse.osbp.vaaclipse.addons.common.api.model.VaaclipseModelUtils;
/**
* The Class VaaclipseModelAssembler.
*/
@SuppressWarnings("restriction")
public class VaaclipseModelAssembler {
/** The logger. */
@Inject
private Logger logger;
/** The application. */
@Inject
private MApplication application;
/** The context. */
@Inject
private IEclipseContext context;
/** The registry. */
@Inject
private IExtensionRegistry registry;
/** The Constant extensionPointID. */
final private static String extensionPointID = "org.eclipse.e4.workbench.model"; //$NON-NLS-1$
/** The Constant INITIAL. */
// private static final String ALWAYS = "always"; //$NON-NLS-1$
private static final String INITIAL = "initial"; //$NON-NLS-1$
/** The Constant NOTEXISTS. */
private static final String NOTEXISTS = "notexists"; //$NON-NLS-1$
/**
* Process the model.
*
* @param initial
* the initial
*/
public void processModel(boolean initial) {
IExtensionPoint extPoint = registry.getExtensionPoint(extensionPointID);
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(extensions, initial, false);
processFragments(extensions, imports, addedElements, initial);
// run processors which are marked to run after fragments
runProcessors(extensions, initial, true);
resolveImports(imports, addedElements);
}
/**
* Process fragments.
*
* @param extensions
* the extensions
* @param imports
* the imports
* @param addedElements
* the added elements
* @param initial
* the initial
*/
private void processFragments(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 (initial || !INITIAL.equals(ce.getAttribute("apply"))) { //$NON-NLS-1$
processFragment(ce, imports, addedElements, initial);
}
}
}
}
}
/**
* Process fragment.
*
* @param ce
* the ce
* @param imports
* the imports
* @param addedElements
* the added elements
* @param initial
* the initial
*/
private void processFragment(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;
}
String cId = r.getID(o);
applicationResource.setID(o, r.getID(o));
String nId = applicationResource.getID(o);
assert(cId.equals(nId));
if (contributorURI != null)
el.setContributorURI(contributorURI);
// 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) eObj)
.setContributorURI(contributorURI);
applicationResource.setID(eObj, r.getInternalId(eObj));
}
}
List<MApplicationElement> merged = merge(application, (MStringModelFragment) fragment);
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);
}
}
}
/**
* Merge.
*
* @param application
* the application
* @param fragment
* the fragment
* @return the list
*/
public List<MApplicationElement> merge(MApplication application, MStringModelFragment fragment) {
MApplicationElement o = VaaclipseModelUtils.findElementById(application, fragment.getParentElementId());
if( o != null ) {
EStructuralFeature feature = ((EObject)o).eClass().getEStructuralFeature(fragment.getFeaturename());
if( feature != null ) {
return VaaclipseModelUtils.merge(o, feature, fragment.getElements(), fragment.getPositionInList());
}
}
return Collections.emptyList();
}
/**
* Run processors.
*
* @param extensions
* the extensions
* @param initial
* the initial
* @param afterFragments
* the after fragments
*/
private void runProcessors(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 (initial || !INITIAL.equals(ce.getAttribute("apply"))) { //$NON-NLS-1$
runProcessor(ce);
}
}
}
}
}
/**
* Run processor.
*
* @param ce
* the 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 = VaaclipseModelUtils
.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$
}
}
/**
* Resolve imports.
*
* @param imports
* the imports
* @param addedElements
* the added elements
*/
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 = VaaclipseModelUtils.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>();
// TODO Probably use EcoreUtil.UsageCrossReferencer
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();
}
}
}