1 /*******************************************************************************
2  * Copyright (c) 2010, 2017 BestSolution.at and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
13  *     Wim Jongman <wim.jongman@remainsoftware.com> - https://bugs.eclipse.org/bugs/show_bug.cgi?id=393150
14  *     Olivier Prouvost <olivier.prouvost@opcoach.com> - Bug 509603
15  ******************************************************************************/
16 package org.eclipse.e4.tools.emf.ui.internal.wbm;
17 
18 import java.io.IOException;
19 
20 import javax.annotation.PreDestroy;
21 import javax.inject.Inject;
22 import javax.inject.Named;
23 
24 import org.eclipse.core.resources.IProject;
25 import org.eclipse.core.resources.IResourceChangeEvent;
26 import org.eclipse.core.resources.IResourceChangeListener;
27 import org.eclipse.core.resources.IResourceDelta;
28 import org.eclipse.core.runtime.IStatus;
29 import org.eclipse.core.runtime.NullProgressMonitor;
30 import org.eclipse.core.runtime.Path;
31 import org.eclipse.core.runtime.Status;
32 import org.eclipse.e4.core.contexts.IEclipseContext;
33 import org.eclipse.e4.core.di.annotations.Optional;
34 import org.eclipse.e4.tools.emf.ui.common.IModelResource;
35 import org.eclipse.e4.tools.emf.ui.internal.common.ModelEditor;
36 import org.eclipse.e4.tools.services.IResourcePool;
37 import org.eclipse.e4.ui.di.UISynchronize;
38 import org.eclipse.e4.ui.model.application.ui.basic.MPart;
39 import org.eclipse.e4.ui.workbench.modeling.EPartService;
40 import org.eclipse.emf.common.util.EList;
41 import org.eclipse.emf.ecore.resource.Resource;
42 import org.eclipse.jface.dialogs.ErrorDialog;
43 import org.eclipse.swt.widgets.Composite;
44 import org.eclipse.swt.widgets.Shell;
45 import org.osgi.framework.FrameworkUtil;
46 
47 public class ApplicationModelEditor extends ModelEditor {
48 
49 	private static final String EDITORPROJECT = "org.eclipse.e4.tools.emf.ui.editorproject"; //$NON-NLS-1$
50 
51 	@Inject
52 	@Optional
53 	MPart part;
54 
55 	@Inject
56 	EPartService partService;
57 
58 	private Resource resource;
59 
60 	private IProject project;
61 
62 	@Inject
63 	Shell shell;
64 
65 	@Inject
66 	UISynchronize sync;
67 
68 	@Inject
ApplicationModelEditor(Composite composite, IEclipseContext context, IModelResource modelProvider, @Named(EDITORPROJECT) @Optional IProject project, IResourcePool resourcePool)69 	public ApplicationModelEditor(Composite composite, IEclipseContext context, IModelResource modelProvider, @Named(EDITORPROJECT) @Optional IProject project, IResourcePool resourcePool) {
70 		super(composite, context, modelProvider, project, resourcePool);
71 		EList<Resource> resources = modelProvider.getEditingDomain().getResourceSet().getResources();
72 		if (!resources.isEmpty()) {
73 			resource = resources.get(0);
74 		}
75 	}
76 
77 	@Inject
addResourceListener(@amedEDITORPROJECT) @ptional IProject project)78 	public void addResourceListener(@Named(EDITORPROJECT) @Optional IProject project) {
79 		if (project != null && resource != null) {
80 			this.project = project;
81 			project.getWorkspace().addResourceChangeListener(listener);
82 		}
83 	}
84 
85 	@PreDestroy
removeResourceListener()86 	private void removeResourceListener() {
87 		if (project != null) {
88 			project.getWorkspace().removeResourceChangeListener(listener);
89 		}
90 	}
91 
92 	/**
93 	 * Listen for changes on the resource being edited. Will close the part if
94 	 * the resource was deleted or the containing project was closed.
95 	 */
96 	private IResourceChangeListener listener = new IResourceChangeListener() {
97 		@Override
98 		public void resourceChanged(IResourceChangeEvent event) {
99 
100 			if (event.getType() == IResourceChangeEvent.PRE_CLOSE || event.getType() == IResourceChangeEvent.PRE_DELETE) {
101 				if (event.getResource().equals(project)) {
102 					hidePart(true);
103 				}
104 				return;
105 			}
106 
107 			if (resource == null) {
108 				return;
109 			}
110 
111 			IResourceDelta delta = event.getDelta().findMember(new Path(resource.getURI().toPlatformString(false)));
112 			if (delta == null) {
113 				return;
114 			}
115 
116 			if (delta.getKind() == IResourceDelta.REMOVED) {
117 				hidePart(true);
118 			}
119 
120 			// If the current model editor is causing this resource change event
121 			// then skip the reload.
122 			if (!isSaving()) {
123 
124 				// reload the model if the file has changed
125 				if ((delta.getKind() == IResourceDelta.CHANGED) && (delta.getMarkerDeltas().length == 0)) {
126 					reloadModel();
127 				}
128 			}
129 		}
130 
131 
132 		private void hidePart(final boolean force) {
133 			sync.asyncExec(() -> partService.hidePart(part, force));
134 		}
135 	};
136 
137 	/**
138 	 * Shows an error dialog based on the passed exception. It should never
139 	 * occur but if it does, the user can report a problem.
140 	 *
141 	 * @param exc
142 	 */
statusDialog(final Exception exc)143 	protected void statusDialog(final Exception exc) {
144 		try {
145 			sync.syncExec(() -> {
146 				String bundle = FrameworkUtil.getBundle(getClass()).getSymbolicName();
147 				Status status = new Status(IStatus.ERROR, bundle, exc.getMessage());
148 				ErrorDialog.openError(shell, exc.getMessage(), exc.getMessage(), status);
149 				exc.printStackTrace(System.err);
150 			});
151 		} catch (Exception e) {
152 		}
153 	}
154 
155 	/**
156 	 * Reload the model.
157 	 */
reloadModel()158 	protected void reloadModel() {
159 		getModelProvider().getRoot().getRealm().asyncExec(() -> {
160 			try {
161 				resource.unload();
162 				resource.load(null);
163 				getModelProvider().replaceRoot(resource.getContents().get(0));
164 				doSave(new NullProgressMonitor());
165 				refreshViewer();
166 			} catch (IOException e) {
167 				statusDialog(e);
168 			}
169 		});
170 	}
171 }