/******************************************************************************* * Copyright (c) 2011 SAP AG 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: * SAP AG - initial API and implementation ******************************************************************************/ package com.sap.furcas.ide.editor.document; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.notify.AdapterFactory; import org.eclipse.emf.common.ui.URIEditorInput; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EcoreFactory; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.Resource.Diagnostic; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.ocl.ecore.opposites.OppositeEndFinder; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.PartInitException; import org.eclipse.ui.PlatformUI; import org.eclipse.ui.part.FileEditorInput; import com.sap.furcas.ide.editor.CtsActivator; import com.sap.furcas.ide.editor.dialogs.SelectEObjectDialog; import com.sap.furcas.metamodel.FURCAS.TCS.ClassTemplate; import com.sap.furcas.metamodel.FURCAS.TCS.ConcreteSyntax; import com.sap.furcas.metamodel.FURCAS.textblocks.TextBlock; import com.sap.furcas.metamodel.FURCAS.textblocks.TextblocksPackage; import com.sap.furcas.metamodel.FURCAS.textblocks.Version; import com.sap.furcas.runtime.tcs.TcsUtil; import com.sap.furcas.runtime.textblocks.TbNavigationUtil; import com.sap.furcas.runtime.textblocks.TbUtil; import com.sap.ocl.oppositefinder.query2.Query2OppositeEndFinder; /** * Helper class that transforms a {@link IEditorInput} into a {@link ModelEditorInput} * by selecting and loading the correct root object and its corresponding root textblock. * * @author Stephan Erb * */ public class ModelEditorInputLoader { private final ResourceSet resourceSet; private final OppositeEndFinder oppositeEndFinder; private final ConcreteSyntax syntax; private final AdapterFactory adapterFactory; public ModelEditorInputLoader(ConcreteSyntax syntax, ResourceSet resourceSet, AdapterFactory adapterFactory) { this.syntax = syntax; this.resourceSet = resourceSet; this.adapterFactory = adapterFactory; // create an opposite end finder that knows about the static resources in the workspace // It is required to find potential textblocks this.oppositeEndFinder = Query2OppositeEndFinder.getInstance(); } /** * Transform the given editor input to a {@link ModelEditorInput}. */ public ModelEditorInput loadEditorInput(IEditorInput input) throws PartInitException { EObject rootElement = null; if (input instanceof FileEditorInput) { // user clicked on a resource within the workspace. Need to find a element // matching the syntax of the editor within this resource IFile file = ((FileEditorInput) input).getFile(); URI uri = URI.createPlatformResourceURI(file.getFullPath().toString(), /*encode*/ true); Resource resource = resourceSet.getResource(uri, /*load*/ true); validateResource(resource); rootElement = findElementWithinResource(resource); } else if (input instanceof URIEditorInput){ // editor is auto-restored on IDE startup. See ModelEditorInput transformes input // into URIEditorInput and that one has factory for restore. URI uri = ((URIEditorInput) input).getURI(); rootElement = resourceSet.getEObject(uri, /*load*/ true); } else { throw new PartInitException("Cannot load editor input " + input.getName() + ". Unsupported input format " + input.getClass().getCanonicalName() + "."); } if (rootElement == null) { throw new PartInitException("Editor input is empty."); } TextBlock rootBlock = findRootBlockForRootObject(rootElement); return new ModelEditorInput(rootElement, rootBlock); } private EObject findElementWithinResource(Resource resource) { Class<?> classOfRootObject = TcsUtil.getMainClassTemplate(syntax).getMetaReference().getInstanceClass(); if (resource.getContents().size() == 0) { return null; } else if (resource.getContents().size() == 1 && classOfRootObject != null && classOfRootObject.isAssignableFrom( resource.getContents().iterator().next().getClass())) { // resource root element is what we are looking for. return resource.getContents().iterator().next(); } else { // open dialog and let the user select the desired element. // only show elements which match the main template of the syntax // or show all elements if instance class of main template could not be found ArrayList<Class<?>> filterList = new ArrayList<Class<?>>(); if (classOfRootObject == null) { filterList.add(EcoreFactory.eINSTANCE.createEObject().getClass()); } else { filterList.add(classOfRootObject); } SelectEObjectDialog diag = new SelectEObjectDialog(PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), resource, filterList, adapterFactory); diag.open(); return diag.getResult(); } } private void validateResource(Resource resource) throws PartInitException { if (resource.getErrors().size() > 0) { StringBuilder message = new StringBuilder(); message.append("Could not load resource: " + resource.getURI().toFileString()); for (Diagnostic diagnostic : resource.getErrors()) { message.append("\n").append(diagnostic.getMessage()); } throw new PartInitException(message.toString()); } } private TextBlock findRootBlockForRootObject(EObject rootObject) throws PartInitException { Collection<EObject> nodes = oppositeEndFinder.navigateOppositePropertyWithBackwardScope( TextblocksPackage.eINSTANCE.getTextBlock_CorrespondingModelElements(), rootObject); if (nodes == null) { return null; } ClassTemplate rootTemplate = TcsUtil.getMainClassTemplate(syntax); // find all potential, existing roots. Collection<TextBlock> rootBlocks = new HashSet<TextBlock>(); Collection<TextBlock> rootBlocksWithBrokenMapping = new HashSet<TextBlock>(); for (EObject object : nodes) { if (object instanceof TextBlock) { TextBlock block = (TextBlock) object; if (!TbNavigationUtil.isUltraRoot(block)) { continue; } if (block.getVersion() != Version.REFERENCE) { continue; } // type can only be checked if mapping is not broken if (TbUtil.isTextBlockOfType(rootTemplate, block)) { rootBlocks.add(block); } else if (block.getType() == null) { rootBlocksWithBrokenMapping.add(block); } } } return selectBlockFromResults(rootObject, rootBlocks, rootBlocksWithBrokenMapping); } /** * TODO: We can relax this in the future. For now we want to crash early * in order to find as many bugs as possible. */ private TextBlock selectBlockFromResults(EObject rootObject, Collection<TextBlock> rootBlocks, Collection<TextBlock> rootBlocksWithBrokenMapping) throws PartInitException { if (rootBlocks.size() == 0 && rootBlocksWithBrokenMapping.size() == 0) { return null; } if (rootBlocks.size() == 1 && rootBlocksWithBrokenMapping.size() == 0) { return rootBlocks.iterator().next(); } if (rootBlocks.size() == 0 && rootBlocksWithBrokenMapping.size() == 1) { return rootBlocksWithBrokenMapping.iterator().next(); } IStatus status = new Status(IStatus.WARNING, CtsActivator.PLUGIN_ID, "Unable to open editor without user intervention: " + "Found several TextBlocks for " + rootObject + " . Cannot defer which one is the desired one."); throw new PartInitException(status); } }