]> git.argeo.org Git - gpl/argeo-slc.git/blob - plugins/org.argeo.slc.client.ui.dist/src/main/java/org/argeo/slc/client/ui/dist/editors/ModularDistVersionOverviewPage.java
069ce94d517b8411320f9b1e3af71995c89139e7
[gpl/argeo-slc.git] / plugins / org.argeo.slc.client.ui.dist / src / main / java / org / argeo / slc / client / ui / dist / editors / ModularDistVersionOverviewPage.java
1 /*
2 * Copyright (C) 2007-2012 Argeo GmbH
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package org.argeo.slc.client.ui.dist.editors;
17
18 import java.net.URL;
19 import java.util.ArrayList;
20 import java.util.HashMap;
21 import java.util.List;
22 import java.util.Map;
23
24 import javax.jcr.Node;
25 import javax.jcr.NodeIterator;
26 import javax.jcr.PropertyType;
27 import javax.jcr.RepositoryException;
28 import javax.jcr.Session;
29 import javax.jcr.query.QueryManager;
30 import javax.jcr.query.QueryResult;
31 import javax.jcr.query.qom.Constraint;
32 import javax.jcr.query.qom.DynamicOperand;
33 import javax.jcr.query.qom.QueryObjectModel;
34 import javax.jcr.query.qom.QueryObjectModelFactory;
35 import javax.jcr.query.qom.Selector;
36 import javax.jcr.query.qom.StaticOperand;
37
38 import org.argeo.eclipse.ui.EclipseUiUtils;
39 import org.argeo.eclipse.ui.utils.CommandUtils;
40 import org.argeo.jcr.JcrUtils;
41 import org.argeo.slc.SlcException;
42 import org.argeo.slc.client.ui.dist.DistConstants;
43 import org.argeo.slc.client.ui.dist.DistImages;
44 import org.argeo.slc.client.ui.dist.commands.OpenModuleEditor;
45 import org.argeo.slc.client.ui.dist.utils.AbstractHyperlinkListener;
46 import org.argeo.slc.client.ui.dist.utils.NodeViewerComparator;
47 import org.argeo.slc.jcr.SlcNames;
48 import org.argeo.slc.jcr.SlcTypes;
49 import org.argeo.slc.repo.RepoConstants;
50 import org.argeo.slc.repo.RepoUtils;
51 import org.argeo.slc.repo.maven.MavenConventionsUtils;
52 import org.eclipse.jface.dialogs.IMessageProvider;
53 import org.eclipse.jface.viewers.ColumnLabelProvider;
54 import org.eclipse.jface.viewers.DoubleClickEvent;
55 import org.eclipse.jface.viewers.IDoubleClickListener;
56 import org.eclipse.jface.viewers.IStructuredContentProvider;
57 import org.eclipse.jface.viewers.IStructuredSelection;
58 import org.eclipse.jface.viewers.TableViewer;
59 import org.eclipse.jface.viewers.TableViewerColumn;
60 import org.eclipse.jface.viewers.Viewer;
61 import org.eclipse.swt.SWT;
62 import org.eclipse.swt.events.ModifyEvent;
63 import org.eclipse.swt.events.ModifyListener;
64 import org.eclipse.swt.events.SelectionAdapter;
65 import org.eclipse.swt.events.SelectionEvent;
66 import org.eclipse.swt.layout.FillLayout;
67 import org.eclipse.swt.layout.GridData;
68 import org.eclipse.swt.layout.GridLayout;
69 import org.eclipse.swt.widgets.Button;
70 import org.eclipse.swt.widgets.Composite;
71 import org.eclipse.swt.widgets.Label;
72 import org.eclipse.swt.widgets.Table;
73 import org.eclipse.swt.widgets.Text;
74 import org.eclipse.ui.PlatformUI;
75 import org.eclipse.ui.browser.IWebBrowser;
76 import org.eclipse.ui.browser.IWorkbenchBrowserSupport;
77 import org.eclipse.ui.forms.IManagedForm;
78 import org.eclipse.ui.forms.editor.FormEditor;
79 import org.eclipse.ui.forms.editor.FormPage;
80 import org.eclipse.ui.forms.events.HyperlinkEvent;
81 import org.eclipse.ui.forms.widgets.FormToolkit;
82 import org.eclipse.ui.forms.widgets.Hyperlink;
83 import org.eclipse.ui.forms.widgets.ScrolledForm;
84 import org.sonatype.aether.artifact.Artifact;
85 import org.sonatype.aether.util.artifact.DefaultArtifact;
86
87 /**
88 * Show all modules contained in a given modular distribution as filter-able
89 * table
90 */
91 public class ModularDistVersionOverviewPage extends FormPage implements
92 SlcNames {
93
94 final static String PAGE_ID = "ModularDistVersionOverviewPage";
95
96 // Business Objects
97 private Node modularDistribution;
98 // private Node modularDistributionBase;
99
100 // This page widgets
101 private NodeViewerComparator comparator;
102 private TableViewer viewer;
103 private FormToolkit tk;
104 private Text filterTxt;
105 private final static String FILTER_HELP_MSG = "Enter filter criterion separated by a space";
106
107 public ModularDistVersionOverviewPage(FormEditor formEditor, String title,
108 Node modularDistribution) {
109 super(formEditor, PAGE_ID, title);
110 this.modularDistribution = modularDistribution;
111 }
112
113 @Override
114 protected void createFormContent(IManagedForm managedForm) {
115 // General settings for this page
116 ScrolledForm form = managedForm.getForm();
117 tk = managedForm.getToolkit();
118 Composite body = form.getBody();
119
120 GridLayout layout = new GridLayout(1, false);
121 layout.marginWidth = 5;
122 layout.marginRight = 15;
123 layout.verticalSpacing = 0;
124 body.setLayout(layout);
125 try {
126 form.setText(modularDistribution.hasProperty(SlcNames.SLC_NAME) ? modularDistribution
127 .getProperty(SlcNames.SLC_NAME).getString() : "");
128 form.setMessage(
129 modularDistribution
130 .hasProperty(DistConstants.SLC_BUNDLE_DESCRIPTION) ? modularDistribution
131 .getProperty(DistConstants.SLC_BUNDLE_DESCRIPTION)
132 .getString() : "", IMessageProvider.NONE);
133 } catch (RepositoryException re) {
134 throw new SlcException("Unable to get bundle name for node "
135 + modularDistribution, re);
136 }
137
138 // Main layout
139 Composite header = tk.createComposite(body);
140 header.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
141 populateHeaderPart(header);
142
143 Composite moduleTablePart = tk.createComposite(body);
144 moduleTablePart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true,
145 true));
146 populateModuleTablePart(moduleTablePart);
147 }
148
149 private void populateHeaderPart(Composite parent) {
150 GridLayout layout = new GridLayout(6, false);
151 layout.horizontalSpacing = 10;
152 parent.setLayout(layout);
153 try {
154 // 1st Line: Category, name version
155 createLT(
156 parent,
157 "Category",
158 modularDistribution.hasProperty(SlcNames.SLC_CATEGORY) ? modularDistribution
159 .getProperty(SlcNames.SLC_CATEGORY).getString()
160 : "");
161 createLT(
162 parent,
163 "Name",
164 modularDistribution.hasProperty(SlcNames.SLC_NAME) ? modularDistribution
165 .getProperty(SlcNames.SLC_NAME).getString() : "");
166 createLT(
167 parent,
168 "Version",
169 modularDistribution.hasProperty(SlcNames.SLC_VERSION) ? modularDistribution
170 .getProperty(SlcNames.SLC_VERSION).getString() : "");
171
172 // 2nd Line: Vendor, licence, sources
173 createLT(
174 parent,
175 "Vendor",
176 modularDistribution
177 .hasProperty(DistConstants.SLC_BUNDLE_VENDOR) ? modularDistribution
178 .getProperty(DistConstants.SLC_BUNDLE_VENDOR)
179 .getString() : "N/A");
180
181 createHyperlink(parent, "Licence", DistConstants.SLC_BUNDLE_LICENCE);
182 addSourceSourcesLink(parent);
183 } catch (RepositoryException re) {
184 throw new SlcException("Unable to get bundle name for node "
185 + modularDistribution, re);
186 }
187
188 }
189
190 private Text createLT(Composite parent, String labelValue, String textValue) {
191 Label label = tk.createLabel(parent, labelValue, SWT.RIGHT);
192 // label.setFont(EclipseUiUtils.getBoldFont(parent));
193 label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false, false));
194
195 // Add a trailing space to workaround a display glitch in RAP 1.3
196 Text text = new Text(parent, SWT.LEFT);
197 text.setText(textValue + " ");
198 text.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false));
199 text.setEditable(false);
200 return text;
201 }
202
203 private void createHyperlink(Composite parent, String label,
204 String jcrPropName) throws RepositoryException {
205 tk.createLabel(parent, label, SWT.NONE);
206 if (modularDistribution.hasProperty(jcrPropName)) {
207 final Hyperlink link = tk.createHyperlink(parent,
208 modularDistribution.getProperty(jcrPropName).getString(),
209 SWT.NONE);
210 link.addHyperlinkListener(new AbstractHyperlinkListener() {
211 @Override
212 public void linkActivated(HyperlinkEvent e) {
213 try {
214 IWorkbenchBrowserSupport browserSupport = PlatformUI
215 .getWorkbench().getBrowserSupport();
216 IWebBrowser browser = browserSupport
217 .createBrowser(
218 IWorkbenchBrowserSupport.LOCATION_BAR
219 | IWorkbenchBrowserSupport.NAVIGATION_BAR,
220 "SLC Distribution browser",
221 "SLC Distribution browser",
222 "A tool tip");
223 browser.openURL(new URL(link.getText()));
224 } catch (Exception ex) {
225 throw new SlcException("error opening browser", ex); //$NON-NLS-1$
226 }
227 }
228 });
229 } else
230 tk.createLabel(parent, "N/A", SWT.NONE);
231 }
232
233 // helper to check if sources are available
234 private void addSourceSourcesLink(Composite parent) {
235 try {
236 String srcPath = RepoUtils.relatedPdeSourcePath(
237 RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH,
238 modularDistribution);
239 if (!modularDistribution.getSession().nodeExists(srcPath)) {
240 createLT(parent, "Sources", "N/A");
241 } else {
242 Node sourcesNode = modularDistribution.getSession().getNode(
243 srcPath);
244
245 String srcName = null;
246 if (sourcesNode.hasProperty(SlcNames.SLC_SYMBOLIC_NAME))
247 srcName = sourcesNode.getProperty(
248 SlcNames.SLC_SYMBOLIC_NAME).getString();
249 else
250 srcName = sourcesNode.getName();
251 Label label = tk.createLabel(parent, "Sources", SWT.RIGHT);
252 label.setLayoutData(new GridData(SWT.RIGHT, SWT.CENTER, false,
253 false));
254 final Hyperlink link = tk.createHyperlink(parent, srcName,
255 SWT.NONE);
256 link.addHyperlinkListener(new AbstractHyperlinkListener() {
257 @Override
258 public void linkActivated(HyperlinkEvent e) {
259 try {
260 System.out.println("CLICK on Sources link");
261 } catch (Exception ex) {
262 throw new SlcException("error opening browser", ex); //$NON-NLS-1$
263 }
264 }
265 });
266
267 }
268 } catch (RepositoryException e) {
269 throw new SlcException("Unable to configure sources link for "
270 + modularDistribution, e);
271 }
272 }
273
274 private void populateModuleTablePart(Composite parent) {
275 GridLayout layout = new GridLayout(1, false);
276 layout.marginWidth = layout.horizontalSpacing = 0;
277 layout.verticalSpacing = 5;
278 layout.marginTop = 15;
279 parent.setLayout(layout);
280 // A sub title
281 Label label = tk.createLabel(parent,
282 "Modules included in the current distribution", SWT.NONE);
283 label.setFont(EclipseUiUtils.getBoldFont(parent));
284
285 // Add the filter section
286 Composite filterPart = tk.createComposite(parent);
287 filterPart.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
288 createFilterPart(filterPart);
289
290 // Add the table
291 Composite tablePart = tk.createComposite(parent);
292 tablePart.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
293 createTableViewer(tablePart);
294 // populate it on first pass.
295 refresh();
296 }
297
298 private void createFilterPart(Composite parent) {
299 GridLayout layout = new GridLayout(2, false);
300 layout.marginWidth = layout.marginHeight = layout.verticalSpacing = 0;
301 layout.horizontalSpacing = 5;
302 parent.setLayout(layout);
303
304 // Text Area to filter
305 filterTxt = tk.createText(parent, "", SWT.BORDER | SWT.SINGLE
306 | SWT.SEARCH | SWT.CANCEL);
307 filterTxt.setMessage(FILTER_HELP_MSG);
308 filterTxt.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
309 filterTxt.addModifyListener(new ModifyListener() {
310 public void modifyText(ModifyEvent event) {
311 refresh();
312 }
313 });
314
315 Button resetBtn = tk.createButton(parent, null, SWT.PUSH);
316 resetBtn.setImage(DistImages.IMG_CLEAR);
317 resetBtn.addSelectionListener(new SelectionAdapter() {
318 public void widgetSelected(SelectionEvent e) {
319 filterTxt.setText("");
320 filterTxt.setMessage(FILTER_HELP_MSG);
321 }
322 });
323 }
324
325 private void createTableViewer(Composite parent) {
326 parent.setLayout(new FillLayout());
327 // helpers to enable sorting by column
328 List<String> propertiesList = new ArrayList<String>();
329 List<Integer> propertyTypesList = new ArrayList<Integer>();
330
331 // Define the TableViewer
332 viewer = new TableViewer(parent, SWT.MULTI | SWT.H_SCROLL
333 | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.BORDER);
334
335 // Name
336 TableViewerColumn col = new TableViewerColumn(viewer, SWT.NONE);
337 col.getColumn().setWidth(220);
338 col.getColumn().setText("Category");
339 col.setLabelProvider(new ColumnLabelProvider() {
340 @Override
341 public String getText(Object element) {
342 return JcrUtils.get((Node) element, SlcNames.SLC_CATEGORY);
343 }
344 });
345 col.getColumn().addSelectionListener(getSelectionAdapter(0));
346 propertiesList.add(SlcNames.SLC_CATEGORY);
347 propertyTypesList.add(PropertyType.STRING);
348
349 // Symbolic name
350 col = new TableViewerColumn(viewer, SWT.NONE);
351 col.getColumn().setWidth(220);
352 col.getColumn().setText("Name");
353 col.setLabelProvider(new ColumnLabelProvider() {
354 @Override
355 public String getText(Object element) {
356 return JcrUtils.get((Node) element, SLC_NAME);
357 }
358 });
359 col.getColumn().addSelectionListener(getSelectionAdapter(1));
360 propertiesList.add(SLC_NAME);
361 propertyTypesList.add(PropertyType.STRING);
362
363 // Version
364 col = new TableViewerColumn(viewer, SWT.NONE);
365 col.getColumn().setWidth(160);
366 col.getColumn().setText("Version");
367 col.setLabelProvider(new ColumnLabelProvider() {
368 @Override
369 public String getText(Object element) {
370 return JcrUtils.get((Node) element, SLC_VERSION);
371 }
372 });
373 col.getColumn().addSelectionListener(getSelectionAdapter(2));
374 propertiesList.add(SLC_VERSION);
375 propertyTypesList.add(PropertyType.STRING);
376
377 // Exists in workspace
378 col = new TableViewerColumn(viewer, SWT.NONE);
379 col.getColumn().setWidth(160);
380 col.getColumn().setText("Exists in workspace");
381 col.setLabelProvider(new ColumnLabelProvider() {
382 @Override
383 public String getText(Object element) {
384 return getRealizedModule((Node) element) != null ? "Yes" : "No";
385 // return JcrUtils.get((Node) element, SLC_VERSION);
386 }
387 });
388 // col.getColumn().addSelectionListener(getSelectionAdapter(2));
389 // propertiesList.add(SLC_VERSION);
390 // propertyTypesList.add(PropertyType.STRING);
391
392 final Table table = viewer.getTable();
393 table.setHeaderVisible(true);
394 table.setLinesVisible(true);
395
396 viewer.setContentProvider(new DistributionsContentProvider());
397 getSite().setSelectionProvider(viewer);
398
399 comparator = new NodeViewerComparator(2,
400 NodeViewerComparator.ASCENDING, propertiesList,
401 propertyTypesList);
402 viewer.setComparator(comparator);
403
404 // // Context Menu
405 // MenuManager menuManager = new MenuManager();
406 // Menu menu = menuManager.createContextMenu(viewer.getTable());
407 // menuManager.addMenuListener(new IMenuListener() {
408 // public void menuAboutToShow(IMenuManager manager) {
409 // contextMenuAboutToShow(manager);
410 // }
411 // });
412 // viewer.getTable().setMenu(menu);
413 // getSite().registerContextMenu(menuManager, viewer);
414
415 // Double click
416 viewer.addDoubleClickListener(new DoubleClickListener());
417 }
418
419 private Node getRealizedModule(Node moduleCoordinates) {
420 try {
421 String category = JcrUtils.get(moduleCoordinates, SLC_CATEGORY);
422 String name = JcrUtils.get(moduleCoordinates, SLC_NAME);
423 String version = JcrUtils.get(moduleCoordinates, SLC_VERSION);
424 Artifact artifact = new DefaultArtifact(category + ":" + name
425 + ":" +version);
426 String parentPath = MavenConventionsUtils.artifactParentPath(
427 RepoConstants.DEFAULT_ARTIFACTS_BASE_PATH, artifact);
428
429 Session session = modularDistribution.getSession();
430 if (session.nodeExists(parentPath)) {
431 Node parent = session.getNode(parentPath);
432 NodeIterator nit = parent.getNodes();
433 while (nit.hasNext()) {
434 Node currN = nit.nextNode();
435 if (currN.isNodeType(SlcTypes.SLC_ARTIFACT))
436 return currN;
437 }
438 }
439 } catch (RepositoryException re) {
440 throw new SlcException(
441 "unable to retrieve realized module with coordinates "
442 + moduleCoordinates, re);
443 }
444 return null;
445 }
446
447 private void refresh() {
448 final List<Node> result = JcrUtils
449 .nodeIteratorToList(listBundleArtifacts());
450 viewer.setInput(result);
451 }
452
453 /** Build repository request */
454 private NodeIterator listBundleArtifacts() {
455 try {
456 Session session = modularDistribution.getSession();
457 QueryManager queryManager = session.getWorkspace()
458 .getQueryManager();
459 QueryObjectModelFactory factory = queryManager.getQOMFactory();
460
461 Selector source = factory.selector(SlcTypes.SLC_MODULE_COORDINATES,
462 SlcTypes.SLC_MODULE_COORDINATES);
463
464 // Create a dynamic operand for each property on which we want to
465 // filter
466 DynamicOperand catDO = factory.propertyValue(
467 source.getSelectorName(), SlcNames.SLC_CATEGORY);
468 DynamicOperand nameDO = factory.propertyValue(
469 source.getSelectorName(), SlcNames.SLC_NAME);
470 DynamicOperand versionDO = factory.propertyValue(
471 source.getSelectorName(), SlcNames.SLC_VERSION);
472
473 String path = modularDistribution.getPath() + "/"
474 + SlcNames.SLC_MODULES;
475
476 // Default Constraint: correct children
477 Constraint defaultC = factory.descendantNode(
478 source.getSelectorName(), path);
479
480 String filter = filterTxt.getText();
481
482 // Build constraints based the textArea content
483 if (filter != null && !"".equals(filter.trim())) {
484 // Parse the String
485 String[] strs = filter.trim().split(" ");
486 for (String token : strs) {
487 token = token.replace('*', '%');
488 StaticOperand so = factory.literal(session
489 .getValueFactory().createValue("%" + token + "%"));
490
491 Constraint currC = factory.comparison(catDO,
492 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so);
493 currC = factory.or(currC, factory.comparison(versionDO,
494 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
495 currC = factory.or(currC, factory.comparison(nameDO,
496 QueryObjectModelFactory.JCR_OPERATOR_LIKE, so));
497
498 defaultC = factory.and(defaultC, currC);
499 }
500 }
501
502 QueryObjectModel query = factory.createQuery(source, defaultC,
503 null, null);
504 QueryResult result = query.execute();
505 return result.getNodes();
506 } catch (RepositoryException re) {
507 throw new SlcException("Unable to refresh module list for node "
508 + modularDistribution, re);
509 }
510 }
511
512 @Override
513 public void setFocus() {
514 viewer.getTable().setFocus();
515 }
516
517 // /** Programmatically configure the context menu */
518 // protected void contextMenuAboutToShow(IMenuManager menuManager) {
519 // IWorkbenchWindow window = DistPlugin.getDefault().getWorkbench()
520 // .getActiveWorkbenchWindow();
521 // // Build conditions
522 // // Delete selected artifacts
523 // // CommandUtils.refreshCommand(menuManager, window, DeleteArtifacts.ID,
524 // // DeleteArtifacts.DEFAULT_LABEL, DeleteArtifacts.DEFAULT_ICON,
525 // // true);
526 // }
527
528 private SelectionAdapter getSelectionAdapter(final int index) {
529 SelectionAdapter selectionAdapter = new SelectionAdapter() {
530 @Override
531 public void widgetSelected(SelectionEvent e) {
532 Table table = viewer.getTable();
533 comparator.setColumn(index);
534 int dir = table.getSortDirection();
535 if (table.getSortColumn() == table.getColumn(index)) {
536 dir = dir == SWT.UP ? SWT.DOWN : SWT.UP;
537 } else {
538 dir = SWT.DOWN;
539 }
540 table.setSortDirection(dir);
541 table.setSortColumn(table.getColumn(index));
542 viewer.refresh();
543 }
544 };
545 return selectionAdapter;
546 }
547
548 /* LOCAL CLASSES */
549 private class DistributionsContentProvider implements
550 IStructuredContentProvider {
551 // we keep a cache of the Nodes in the content provider to be able to
552 // manage long request
553 private List<Node> nodes;
554
555 public void dispose() {
556 }
557
558 // We expect a list of nodes as a new input
559 @SuppressWarnings("unchecked")
560 public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
561 nodes = (List<Node>) newInput;
562 }
563
564 public Object[] getElements(Object arg0) {
565 return nodes.toArray();
566 }
567 }
568
569 private class DoubleClickListener implements IDoubleClickListener {
570
571 public void doubleClick(DoubleClickEvent event) {
572 Object obj = ((IStructuredSelection) event.getSelection())
573 .getFirstElement();
574 if (obj instanceof Node) {
575 Node node = (Node) obj;
576 try {
577 if (node.isNodeType(SlcTypes.SLC_MODULE_COORDINATES)) {
578 Node realizedModule = getRealizedModule(node);
579 if (realizedModule != null) {
580 ModuleEditorInput dwip = (ModuleEditorInput) getEditorInput();
581 Map<String, String> params = new HashMap<String, String>();
582 params.put(OpenModuleEditor.PARAM_REPO_NODE_PATH,
583 dwip.getRepoNodePath());
584 params.put(OpenModuleEditor.PARAM_REPO_URI,
585 dwip.getUri());
586 params.put(OpenModuleEditor.PARAM_WORKSPACE_NAME,
587 dwip.getWorkspaceName());
588 String path = realizedModule.getPath();
589 params.put(OpenModuleEditor.PARAM_MODULE_PATH, path);
590 CommandUtils.callCommand(OpenModuleEditor.ID,
591 params);
592 }
593 }
594 } catch (RepositoryException re) {
595 throw new SlcException("Cannot get path for node " + node
596 + " while setting parameters for "
597 + "command OpenModuleEditor", re);
598 }
599 }
600 }
601 }
602 }