/******************************************************************************* * Copyright (c) 2012 Association for Decentralized Information Management * in Industry THTH ry. * 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: * VTT Technical Research Centre of Finland - initial API and implementation *******************************************************************************/ package org.simantics.modeling.ui.actions; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.concurrent.atomic.AtomicReference; import org.eclipse.jface.dialogs.Dialog; import org.eclipse.jface.viewers.ICheckStateProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.jface.viewers.Viewer; import org.eclipse.ui.PlatformUI; import org.simantics.Simantics; import org.simantics.db.ReadGraph; import org.simantics.db.Resource; import org.simantics.db.common.request.PossibleIndexRoot; import org.simantics.db.common.request.UniqueRead; import org.simantics.db.common.utils.NameUtils; import org.simantics.db.exception.DatabaseException; import org.simantics.db.layer0.adapter.ActionFactory; import org.simantics.db.layer0.adapter.ActionFactory2; import org.simantics.db.request.Read; import org.simantics.diagram.stubs.DiagramResource; import org.simantics.modeling.AssignConnectionTypesRequest; import org.simantics.modeling.GetConnectionTypes; import org.simantics.structural2.modelingRules.AllowedConnectionTypes; import org.simantics.utils.strings.AlphanumComparator; import org.simantics.utils.ui.ErrorLogger; import org.simantics.utils.ui.dialogs.ShowMessage; /** * @author Antti Villberg */ public class ConfigureConnectionTypes implements ActionFactory, ActionFactory2 { @Override public Runnable create(Collection targets) { final ArrayList resources = new ArrayList(); for (Object target : targets) { if (!(target instanceof Resource)) return null; resources.add((Resource) target); } return new Runnable() { @Override public void run() { assignTypes(resources); } }; } @Override public Runnable create(Object target) { if(!(target instanceof Resource)) return null; final Resource connectionPoint = (Resource)target; return new Runnable() { @Override public void run() { assignTypes(Collections.singletonList(connectionPoint)); } }; } private static final ConnectionType[] NO_CONNECTION_TYPES = new ConnectionType[0]; static enum Tristate { NONE, SOME, ALL; public static Tristate add(Tristate current, boolean next) { if (current == null) return next ? ALL : NONE; switch (current) { case ALL: return next ? ALL : SOME; case SOME: return next ? SOME : SOME; case NONE: return next ? SOME : NONE; default: return NONE; } } } private static class ConnectionType implements Comparable { Resource resource; String name; Tristate originallySelected; Tristate selected; public ConnectionType(Resource resource, String name, Tristate originallySelected, Tristate selected) { super(); this.resource = resource; this.name = name; this.originallySelected = originallySelected; this.selected = selected; } @Override public int compareTo(ConnectionType o) { return AlphanumComparator.CASE_INSENSITIVE_COMPARATOR.compare(name, o.name); } @Override public String toString() { return getClass().getSimpleName() + "[name=" + name //$NON-NLS-1$ + ", originally selected=" + originallySelected //$NON-NLS-1$ + ", selected=" + selected + "]"; //$NON-NLS-1$ //$NON-NLS-2$ } } private static class ContentProviderImpl implements IStructuredContentProvider { @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } @Override public void dispose() { } @Override public Object[] getElements(Object inputElement) { return (Object[])inputElement; } }; private static class LabelProviderImpl extends LabelProvider { @Override public String getText(Object element) { return ((ConnectionType)element).name; } } private static class CheckStateProviderImpl implements ICheckStateProvider { @Override public boolean isChecked(Object element) { return ((ConnectionType) element).selected != Tristate.NONE; } @Override public boolean isGrayed(Object element) { return ((ConnectionType) element).selected == Tristate.SOME; } } private static Resource getCommonModel(final Collection connectionPoints) { try { return Simantics.sync(new UniqueRead() { @Override public Resource perform(ReadGraph graph) throws DatabaseException { return getPossibleIndexRoot(graph, connectionPoints); } }); } catch (DatabaseException e) { ErrorLogger.defaultLogError(e); return null; } } private static Resource getPossibleIndexRoot(ReadGraph g, Collection connectionPoints) throws DatabaseException { Resource model = null; for (Resource connectionPoint : connectionPoints) { Resource m = getIndexRootOf(g, connectionPoint); if (m == null) return null; if (model == null) model = m; else if (!model.equals(m)) return null; } return model; } private static Resource getIndexRootOf(ReadGraph g, Resource connectionPoint) throws DatabaseException { return g.syncRequest(new PossibleIndexRoot(connectionPoint)); } private static ConnectionType[] getConnectionTypes(final Collection connectionPoints) { try { return Simantics.getSession().syncRequest(new Read() { @Override public ConnectionType[] perform(ReadGraph g) throws DatabaseException { return getConnectionTypes(g, connectionPoints); } }); } catch(DatabaseException e) { e.printStackTrace(); return NO_CONNECTION_TYPES; } } private static ConnectionType[] getConnectionTypes(ReadGraph g, Collection connectionPoints) throws DatabaseException { Resource root = getPossibleIndexRoot(g, connectionPoints); if (root == null) return NO_CONNECTION_TYPES; // All connection points have same index root. // Resolve the connection type selection states now. ArrayList result = new ArrayList(); DiagramResource DIA = DiagramResource.getInstance(g); for (Resource type : GetConnectionTypes.getConnectionTypes(g, root)) { Tristate selected = getConnectionTypeSelectionState(g, type, connectionPoints, DIA); selected = selected != null ? selected : Tristate.NONE; result.add( new ConnectionType( type, NameUtils.getSafeLabel(g, type), selected, selected) ); } //System.out.println("result: " + EString.implode(result)); Collections.sort(result); //System.out.println("sorted result: " + EString.implode(result)); return result.toArray(new ConnectionType[result.size()]); } protected static Tristate getConnectionTypeSelectionState(ReadGraph graph, Resource connectionType, Collection connectionPoints, DiagramResource DIA) throws DatabaseException { Tristate selected = null; for (Resource connectionPoint : connectionPoints) { Collection allowed = graph.syncRequest(new AllowedConnectionTypes(connectionPoint)); selected = Tristate.add(selected, allowed.contains(connectionType)); } return selected != null ? selected : Tristate.NONE; } private static ConnectionType[] selectedElements(ConnectionType[] connectionTypes) { int count = 0; for(ConnectionType connectionType : connectionTypes) if(connectionType.selected != Tristate.NONE) ++count; ConnectionType[] result = new ConnectionType[count]; count = 0; for(ConnectionType connectionType : connectionTypes) if(connectionType.selected != Tristate.NONE) result[count++] = connectionType; return result; } public void assignTypes(final Collection connectionPoints) { if (connectionPoints.isEmpty()) return; final Resource indexRoot = getCommonModel(connectionPoints); if (indexRoot == null) { ShowMessage.showInformation(Messages.ConfigureConnectionTypes_SameModelRequired, Messages.ConfigureConnectionTypes_SameModelRequiredMsg); return; } final AtomicReference types = new AtomicReference( getConnectionTypes(connectionPoints) ); StringBuilder message = new StringBuilder(); if (connectionPoints.size() > 1) message.append(Messages.ConfigureConnectionTypes_SelectConnectionTypeForSelectedConnectionPoints); else message.append(Messages.ConfigureConnectionTypes_SelectConnectionTypeForSelectedConnectionPoint); ConfigureConnectionTypesDialog dialog = new ConfigureConnectionTypesDialog( PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(), types.get(), new ContentProviderImpl(), new LabelProviderImpl(), new CheckStateProviderImpl(), message.toString()) { @Override protected void checkStateChanged(Object[] elements, boolean checked) { for (Object _g : elements) { ConnectionType g = (ConnectionType) _g; g.selected = checked ? Tristate.ALL : Tristate.NONE; // Refresh checked states through provider. listViewer.refresh(); } } }; dialog.setTitle(Messages.ConfigureConnectionTypes_ConnectionTypeAssignments); dialog.setInitialSelections(selectedElements(types.get())); if (dialog.open() == Dialog.OK) { final ArrayList added = new ArrayList(); final ArrayList removed = new ArrayList(); for (ConnectionType g : types.get()) { if (g.selected != g.originallySelected && g.selected == Tristate.ALL) added.add(g); if (g.selected != g.originallySelected && g.selected == Tristate.NONE) removed.add(g); } if (!added.isEmpty() || !removed.isEmpty()) { ArrayList addedConnectionTypes = new ArrayList(); ArrayList removedConnectionTypes = new ArrayList(); for (ConnectionType type : added) addedConnectionTypes.add(type.resource); for (ConnectionType type : removed) removedConnectionTypes.add(type.resource); Simantics.getSession().asyncRequest(new AssignConnectionTypesRequest(addedConnectionTypes, removedConnectionTypes, connectionPoints)); } } } }