1 /*******************************************************************************
2 * Copyright (c) 2012 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.modeling.ui.actions;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.concurrent.atomic.AtomicReference;
19 import org.eclipse.jface.dialogs.Dialog;
20 import org.eclipse.jface.viewers.ICheckStateProvider;
21 import org.eclipse.jface.viewers.IStructuredContentProvider;
22 import org.eclipse.jface.viewers.LabelProvider;
23 import org.eclipse.jface.viewers.Viewer;
24 import org.eclipse.ui.PlatformUI;
25 import org.simantics.Simantics;
26 import org.simantics.db.ReadGraph;
27 import org.simantics.db.Resource;
28 import org.simantics.db.common.request.PossibleIndexRoot;
29 import org.simantics.db.common.request.UniqueRead;
30 import org.simantics.db.common.utils.NameUtils;
31 import org.simantics.db.exception.DatabaseException;
32 import org.simantics.db.layer0.adapter.ActionFactory;
33 import org.simantics.db.layer0.adapter.ActionFactory2;
34 import org.simantics.db.request.Read;
35 import org.simantics.diagram.stubs.DiagramResource;
36 import org.simantics.modeling.AssignConnectionTypesRequest;
37 import org.simantics.modeling.GetConnectionTypes;
38 import org.simantics.structural2.modelingRules.AllowedConnectionTypes;
39 import org.simantics.utils.strings.AlphanumComparator;
40 import org.simantics.utils.ui.ErrorLogger;
41 import org.simantics.utils.ui.dialogs.ShowMessage;
44 * @author Antti Villberg
46 public class ConfigureConnectionTypes implements ActionFactory, ActionFactory2 {
49 public Runnable create(Collection<?> targets) {
50 final ArrayList<Resource> resources = new ArrayList<Resource>();
51 for (Object target : targets) {
52 if (!(target instanceof Resource))
54 resources.add((Resource) target);
56 return new Runnable() {
59 assignTypes(resources);
65 public Runnable create(Object target) {
66 if(!(target instanceof Resource))
68 final Resource connectionPoint = (Resource)target;
69 return new Runnable() {
72 assignTypes(Collections.singletonList(connectionPoint));
77 private static final ConnectionType[] NO_CONNECTION_TYPES = new ConnectionType[0];
79 static enum Tristate {
82 public static Tristate add(Tristate current, boolean next) {
84 return next ? ALL : NONE;
86 case ALL: return next ? ALL : SOME;
87 case SOME: return next ? SOME : SOME;
88 case NONE: return next ? SOME : NONE;
94 private static class ConnectionType implements Comparable<ConnectionType> {
97 Tristate originallySelected;
100 public ConnectionType(Resource resource, String name, Tristate originallySelected, Tristate selected) {
102 this.resource = resource;
104 this.originallySelected = originallySelected;
105 this.selected = selected;
109 public int compareTo(ConnectionType o) {
110 return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(name, o.name);
114 public String toString() {
115 return getClass().getSimpleName() + "[name=" + name
116 + ", originally selected=" + originallySelected
117 + ", selected=" + selected + "]";
121 private static class ContentProviderImpl implements IStructuredContentProvider {
123 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
127 public void dispose() {
131 public Object[] getElements(Object inputElement) {
132 return (Object[])inputElement;
136 private static class LabelProviderImpl extends LabelProvider {
138 public String getText(Object element) {
139 return ((ConnectionType)element).name;
143 private static class CheckStateProviderImpl implements ICheckStateProvider {
145 public boolean isChecked(Object element) {
146 return ((ConnectionType) element).selected != Tristate.NONE;
149 public boolean isGrayed(Object element) {
150 return ((ConnectionType) element).selected == Tristate.SOME;
154 private static Resource getCommonModel(final Collection<Resource> connectionPoints) {
156 return Simantics.sync(new UniqueRead<Resource>() {
158 public Resource perform(ReadGraph graph) throws DatabaseException {
159 return getPossibleIndexRoot(graph, connectionPoints);
162 } catch (DatabaseException e) {
163 ErrorLogger.defaultLogError(e);
168 private static Resource getPossibleIndexRoot(ReadGraph g, Collection<Resource> connectionPoints) throws DatabaseException {
169 Resource model = null;
170 for (Resource connectionPoint : connectionPoints) {
171 Resource m = getIndexRootOf(g, connectionPoint);
176 else if (!model.equals(m))
182 private static Resource getIndexRootOf(ReadGraph g, Resource connectionPoint) throws DatabaseException {
183 return g.syncRequest(new PossibleIndexRoot(connectionPoint));
186 private static ConnectionType[] getConnectionTypes(final Collection<Resource> connectionPoints) {
188 return Simantics.getSession().syncRequest(new Read<ConnectionType[]>() {
190 public ConnectionType[] perform(ReadGraph g) throws DatabaseException {
191 return getConnectionTypes(g, connectionPoints);
194 } catch(DatabaseException e) {
196 return NO_CONNECTION_TYPES;
200 private static ConnectionType[] getConnectionTypes(ReadGraph g, Collection<Resource> connectionPoints) throws DatabaseException {
201 Resource root = getPossibleIndexRoot(g, connectionPoints);
203 return NO_CONNECTION_TYPES;
204 // All connection points have same index root.
205 // Resolve the connection type selection states now.
206 ArrayList<ConnectionType> result = new ArrayList<ConnectionType>();
207 DiagramResource DIA = DiagramResource.getInstance(g);
208 for (Resource type : GetConnectionTypes.getConnectionTypes(g, root)) {
209 Tristate selected = getConnectionTypeSelectionState(g, type, connectionPoints, DIA);
210 selected = selected != null ? selected : Tristate.NONE;
211 result.add( new ConnectionType(
213 NameUtils.getSafeLabel(g, type),
217 //System.out.println("result: " + EString.implode(result));
218 Collections.sort(result);
219 //System.out.println("sorted result: " + EString.implode(result));
220 return result.toArray(new ConnectionType[result.size()]);
223 protected static Tristate getConnectionTypeSelectionState(ReadGraph graph, Resource connectionType,
224 Collection<Resource> connectionPoints, DiagramResource DIA) throws DatabaseException {
225 Tristate selected = null;
226 for (Resource connectionPoint : connectionPoints) {
227 Collection<Resource> allowed = graph.syncRequest(new AllowedConnectionTypes(connectionPoint));
228 selected = Tristate.add(selected, allowed.contains(connectionType));
230 return selected != null ? selected : Tristate.NONE;
233 private static ConnectionType[] selectedElements(ConnectionType[] connectionTypes) {
235 for(ConnectionType connectionType : connectionTypes)
236 if(connectionType.selected != Tristate.NONE)
238 ConnectionType[] result = new ConnectionType[count];
240 for(ConnectionType connectionType : connectionTypes)
241 if(connectionType.selected != Tristate.NONE)
242 result[count++] = connectionType;
246 public void assignTypes(final Collection<Resource> connectionPoints) {
247 if (connectionPoints.isEmpty())
250 final Resource indexRoot = getCommonModel(connectionPoints);
251 if (indexRoot == null) {
252 ShowMessage.showInformation("Same Model Required", "All the selected connection points must be from within the same index root.");
256 final AtomicReference<ConnectionType[]> types =
257 new AtomicReference<ConnectionType[]>( getConnectionTypes(connectionPoints) );
259 StringBuilder message = new StringBuilder();
260 if (connectionPoints.size() > 1)
261 message.append("Select connection types for the selected connection points");
263 message.append("Select connection types for the selected connection point");
265 ConfigureConnectionTypesDialog dialog = new ConfigureConnectionTypesDialog(
266 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
268 new ContentProviderImpl(),
269 new LabelProviderImpl(),
270 new CheckStateProviderImpl(),
271 message.toString()) {
274 protected void checkStateChanged(Object[] elements, boolean checked) {
275 for (Object _g : elements) {
276 ConnectionType g = (ConnectionType) _g;
277 g.selected = checked ? Tristate.ALL : Tristate.NONE;
278 // Refresh checked states through provider.
279 listViewer.refresh();
284 dialog.setTitle("Connection Type Assignments");
285 dialog.setInitialSelections(selectedElements(types.get()));
286 if (dialog.open() == Dialog.OK) {
287 final ArrayList<ConnectionType> added = new ArrayList<ConnectionType>();
288 final ArrayList<ConnectionType> removed = new ArrayList<ConnectionType>();
289 for (ConnectionType g : types.get()) {
290 if (g.selected != g.originallySelected && g.selected == Tristate.ALL)
292 if (g.selected != g.originallySelected && g.selected == Tristate.NONE)
295 if (!added.isEmpty() || !removed.isEmpty()) {
296 ArrayList<Resource> addedConnectionTypes = new ArrayList<Resource>();
297 ArrayList<Resource> removedConnectionTypes = new ArrayList<Resource>();
298 for (ConnectionType type : added)
299 addedConnectionTypes.add(type.resource);
300 for (ConnectionType type : removed)
301 removedConnectionTypes.add(type.resource);
302 Simantics.getSession().asyncRequest(new AssignConnectionTypesRequest(addedConnectionTypes, removedConnectionTypes, connectionPoints));