]> gerrit.simantics Code Review - simantics/platform.git/blob - bundles/org.simantics.modeling.ui/src/org/simantics/modeling/ui/actions/ConfigureConnectionTypes.java
6f57f86d61f4d3e6de234b87660f2e183c1a7dd2
[simantics/platform.git] / bundles / org.simantics.modeling.ui / src / org / simantics / modeling / ui / actions / ConfigureConnectionTypes.java
1 /*******************************************************************************
2  * Copyright (c) 2012 Association for Decentralized Information Management
3  * in Industry THTH ry.
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
8  *
9  * Contributors:
10  *     VTT Technical Research Centre of Finland - initial API and implementation
11  *******************************************************************************/
12 package org.simantics.modeling.ui.actions;
13
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.Collections;
17 import java.util.concurrent.atomic.AtomicReference;
18
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;
42
43 /**
44  * @author Antti Villberg
45  */
46 public class ConfigureConnectionTypes implements ActionFactory, ActionFactory2 {
47
48     @Override
49     public Runnable create(Collection<?> targets) {
50         final ArrayList<Resource> resources = new ArrayList<Resource>();
51         for (Object target : targets) {
52             if (!(target instanceof Resource))
53                 return null;
54             resources.add((Resource) target);
55         }
56         return new Runnable() {
57             @Override
58             public void run() {
59                 assignTypes(resources);
60             }
61         };
62     }
63
64     @Override
65     public Runnable create(Object target) {
66         if(!(target instanceof Resource))
67             return null;
68         final Resource connectionPoint = (Resource)target;
69         return new Runnable() {
70             @Override
71             public void run() {
72                 assignTypes(Collections.singletonList(connectionPoint));
73             }
74         };
75     }
76
77     private static final ConnectionType[] NO_CONNECTION_TYPES = new ConnectionType[0];
78
79     static enum Tristate {
80         NONE, SOME, ALL;
81
82         public static Tristate add(Tristate current, boolean next) {
83             if (current == null)
84                 return next ? ALL : NONE;
85             switch (current) {
86             case ALL: return next ? ALL : SOME; 
87             case SOME: return next ? SOME : SOME;
88             case NONE: return next ? SOME : NONE;
89             default: return NONE;
90             }
91         }
92     }
93
94     private static class ConnectionType implements Comparable<ConnectionType> {
95         Resource resource;
96         String name;
97         Tristate originallySelected;
98         Tristate selected;
99
100         public ConnectionType(Resource resource, String name, Tristate originallySelected, Tristate selected) {
101             super();
102             this.resource = resource;
103             this.name = name;
104             this.originallySelected = originallySelected;
105             this.selected = selected;
106         }
107
108         @Override
109         public int compareTo(ConnectionType o) {
110             return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(name, o.name);
111         }
112
113         @Override
114         public String toString() {
115             return getClass().getSimpleName() + "[name=" + name
116                     + ", originally selected=" + originallySelected
117                     + ", selected=" + selected + "]";
118         }
119     }
120
121     private static class ContentProviderImpl implements IStructuredContentProvider {    
122         @Override
123         public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
124         }
125
126         @Override
127         public void dispose() {
128         }
129
130         @Override
131         public Object[] getElements(Object inputElement) {
132             return (Object[])inputElement;
133         }
134     };
135
136     private static class LabelProviderImpl extends LabelProvider {
137         @Override
138         public String getText(Object element) {
139             return ((ConnectionType)element).name;
140         }
141     }
142
143     private static class CheckStateProviderImpl implements ICheckStateProvider {
144         @Override
145         public boolean isChecked(Object element) {
146             return ((ConnectionType) element).selected != Tristate.NONE;
147         }
148         @Override
149         public boolean isGrayed(Object element) {
150             return ((ConnectionType) element).selected == Tristate.SOME;
151         }
152     }
153
154     private static Resource getCommonModel(final Collection<Resource> connectionPoints) {
155         try {
156             return Simantics.sync(new UniqueRead<Resource>() {
157                 @Override
158                 public Resource perform(ReadGraph graph) throws DatabaseException {
159                     return getPossibleIndexRoot(graph, connectionPoints);
160                 }
161             });
162         } catch (DatabaseException e) {
163             ErrorLogger.defaultLogError(e);
164             return null;
165         }
166     }
167
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);
172             if (m == null)
173                 return null;
174             if (model == null)
175                 model = m;
176             else if (!model.equals(m))
177                 return null;
178         }
179         return model;
180     }
181
182     private static Resource getIndexRootOf(ReadGraph g, Resource connectionPoint) throws DatabaseException {
183         return g.syncRequest(new PossibleIndexRoot(connectionPoint));
184     }
185
186     private static ConnectionType[] getConnectionTypes(final Collection<Resource> connectionPoints) {
187         try {
188             return Simantics.getSession().syncRequest(new Read<ConnectionType[]>() {
189                 @Override
190                 public ConnectionType[] perform(ReadGraph g) throws DatabaseException {
191                     return getConnectionTypes(g, connectionPoints);
192                 }
193             });
194         } catch(DatabaseException e) {
195             e.printStackTrace();
196             return NO_CONNECTION_TYPES;
197         }
198     }
199
200     private static ConnectionType[] getConnectionTypes(ReadGraph g, Collection<Resource> connectionPoints) throws DatabaseException {
201         Resource root = getPossibleIndexRoot(g, connectionPoints);
202         if (root == null)
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(
212                     type,
213                     NameUtils.getSafeLabel(g, type),
214                     selected,
215                     selected) );
216         }
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()]);
221     }
222
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));
229         }
230         return selected != null ? selected : Tristate.NONE;
231     }
232
233     private static ConnectionType[] selectedElements(ConnectionType[] connectionTypes) {
234         int count = 0;
235         for(ConnectionType connectionType : connectionTypes)
236             if(connectionType.selected != Tristate.NONE)
237                 ++count;
238         ConnectionType[] result = new ConnectionType[count];
239         count = 0;
240         for(ConnectionType connectionType : connectionTypes)
241             if(connectionType.selected != Tristate.NONE)
242                 result[count++] = connectionType;
243         return result;
244     }
245
246     public void assignTypes(final Collection<Resource> connectionPoints) {
247         if (connectionPoints.isEmpty())
248             return;
249
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.");
253             return;
254         }
255
256         final AtomicReference<ConnectionType[]> types =
257                 new AtomicReference<ConnectionType[]>( getConnectionTypes(connectionPoints) );
258
259         StringBuilder message = new StringBuilder();
260         if (connectionPoints.size() > 1)
261             message.append("Select connection types for the selected connection points");
262         else
263             message.append("Select connection types for the selected connection point");
264
265         ConfigureConnectionTypesDialog dialog = new ConfigureConnectionTypesDialog(
266                 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(),
267                 types.get(),
268                 new ContentProviderImpl(), 
269                 new LabelProviderImpl(), 
270                 new CheckStateProviderImpl(),
271                 message.toString()) {
272
273             @Override
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();
280                 }
281             }
282
283         };
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)
291                     added.add(g);
292                 if (g.selected != g.originallySelected && g.selected == Tristate.NONE)
293                     removed.add(g);
294             }
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));
303             }
304         }
305     }
306
307 }