Merge branch 'master' into lttng_2_0_control_dev
[deliverable/tracecompass.git] / org.eclipse.linuxtools.lttng.ui / src / org / eclipse / linuxtools / lttng / ui / views / control / dialogs / EnableUstEventsComposite.java
1 /**********************************************************************
2 * Copyright (c) 2012 Ericsson
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Bernd Hufmann - Initial API and implementation
11 **********************************************************************/
12 package org.eclipse.linuxtools.lttng.ui.views.control.dialogs;
13
14 import java.util.ArrayList;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Set;
18
19 import org.eclipse.jface.dialogs.MessageDialog;
20 import org.eclipse.jface.viewers.CheckStateChangedEvent;
21 import org.eclipse.jface.viewers.CheckboxTreeViewer;
22 import org.eclipse.jface.viewers.ICheckStateListener;
23 import org.eclipse.linuxtools.lttng.ui.views.control.Messages;
24 import org.eclipse.linuxtools.lttng.ui.views.control.TraceControlContentProvider;
25 import org.eclipse.linuxtools.lttng.ui.views.control.TraceControlLabelProvider;
26 import org.eclipse.linuxtools.lttng.ui.views.control.model.ITraceControlComponent;
27 import org.eclipse.linuxtools.lttng.ui.views.control.model.LogLevelType;
28 import org.eclipse.linuxtools.lttng.ui.views.control.model.TraceLogLevel;
29 import org.eclipse.linuxtools.lttng.ui.views.control.model.impl.BaseEventComponent;
30 import org.eclipse.linuxtools.lttng.ui.views.control.model.impl.TargetNodeComponent;
31 import org.eclipse.linuxtools.lttng.ui.views.control.model.impl.TraceProviderGroup;
32 import org.eclipse.linuxtools.lttng.ui.views.control.model.impl.UstProviderComponent;
33 import org.eclipse.swt.SWT;
34 import org.eclipse.swt.custom.CCombo;
35 import org.eclipse.swt.events.SelectionAdapter;
36 import org.eclipse.swt.events.SelectionEvent;
37 import org.eclipse.swt.graphics.Image;
38 import org.eclipse.swt.layout.GridData;
39 import org.eclipse.swt.layout.GridLayout;
40 import org.eclipse.swt.widgets.Button;
41 import org.eclipse.swt.widgets.Composite;
42 import org.eclipse.swt.widgets.Group;
43 import org.eclipse.swt.widgets.Label;
44 import org.eclipse.swt.widgets.Text;
45
46 /**
47 * <b><u>EnableUstEventsComposite</u></b>
48 * <p>
49 * A composite for collecting information about UST events to be enabled.
50 * </p>
51 */
52 public class EnableUstEventsComposite extends Composite implements IEnableUstEvents {
53
54 // ------------------------------------------------------------------------
55 // Constants
56 // ------------------------------------------------------------------------
57
58 private enum GroupEnum { TRACEPOINTS, WILDCARD, LOGLEVEL };
59
60 // ------------------------------------------------------------------------
61 // Attributes
62 // ------------------------------------------------------------------------
63
64 /**
65 * A button to enable/disable the tracepoints group
66 */
67 private Button fTracepointsActivateButton;
68 /**
69 * A tree viewer for diplaying and selection of available tracepoints.
70 */
71 private CheckboxTreeViewer fTracepointsViewer;
72 /**
73 * A button to enable/disable the wildcard group
74 */
75 private Button fWildcardActivateButton;
76 /**
77 * A Text field for the event's wildcard.
78 */
79 private Text fWildcardText;
80 /**
81 * A button to enable/disable the log level group
82 */
83 private Button fLogLevelActivateButton;
84 /**
85 * A Text field for the event name for the log level enablement.
86 */
87 private Text fLogLevelEventNameText;
88 /**
89 * A Combo box for selecting the log level.
90 */
91 private CCombo fLogLevelCombo;
92 /**
93 * A button for selecting the log level (range 0 to level).
94 */
95 private Button fLogLevelButton;
96 /**
97 * A button for selecting the specified log level only.
98 */
99 private Button fLogLevelOnlyButton;
100 /**
101 * The referenced trace provider group containing the UST providers
102 * component which contains a list of available tracepoints.
103 */
104 private TraceProviderGroup fProviderGroup;
105 /**
106 * The flag indicating that tracepoints are selected.
107 */
108 private boolean fIsTracepoints;
109 /**
110 * The flag indicating that all tracepoints (across providers) are selected.
111 */
112 private boolean fIsAllTracepoints;
113 /**
114 * The list of tracepoints to be enabled.
115 */
116 private List<String> fSelectedEvents;
117 /**
118 * The flag indicating that all wildcard are selected..
119 */
120 private boolean fIsWildcard;
121 /**
122 * The wildcard if wildcard is selected.
123 */
124 private String fWildcard;
125 /**
126 *The flag indicating that all log level are selected.
127 */
128 private boolean fIsLogLevel;
129 /**
130 * The event name for the log level enablement.
131 */
132 private String fLogLevelEventName;
133 /**
134 * The type of the log level (loglevel or loglevel-only)
135 */
136 private LogLevelType fLogLevelType;
137 /**
138 * The actual selected log level.
139 */
140 private TraceLogLevel fLogLevel;
141
142 // ------------------------------------------------------------------------
143 // Constructors
144 // ------------------------------------------------------------------------
145 /**
146 * Constructor
147 * @param parent - a parent composite
148 * @Param style - a compsite style
149 * @param providerGroup - the trace provider group
150 */
151 public EnableUstEventsComposite(Composite parent, int style, TraceProviderGroup providerGroup) {
152 super(parent, style);
153 fProviderGroup = providerGroup;
154 }
155
156 // ------------------------------------------------------------------------
157 // Accessors
158 // ------------------------------------------------------------------------
159
160 /*
161 * (non-Javadoc)
162 * @see org.eclipse.linuxtools.lttng.ui.views.control.dialogs.IEnableUstEvents#isTracepoints()
163 */
164 @Override
165 public boolean isTracepoints() {
166 return fIsTracepoints;
167 }
168
169 /*
170 * (non-Javadoc)
171 * @see org.eclipse.linuxtools.lttng.ui.views.control.dialogs.IEnableUstEvents#isAllTracePoints()
172 */
173 @Override
174 public boolean isAllTracePoints() {
175 return fIsAllTracepoints;
176 }
177
178 /*
179 * (non-Javadoc)
180 * @see org.eclipse.linuxtools.lttng.ui.views.control.dialogs.IEnableUstEvents#getEventNames()
181 */
182 @Override
183 public List<String> getEventNames() {
184 return new ArrayList<String>(fSelectedEvents);
185 }
186
187 /*
188 * (non-Javadoc)
189 * @see org.eclipse.linuxtools.lttng.ui.views.control.dialogs.IEnableUstEvents#isWildcard()
190 */
191 @Override
192 public boolean isWildcard() {
193 return fIsWildcard;
194 }
195
196 /*
197 * (non-Javadoc)
198 * @see org.eclipse.linuxtools.lttng.ui.views.control.dialogs.IEnableUstEvents#getWildcard()
199 */
200 @Override
201 public String getWildcard() {
202 return "\"" + fWildcard + "\""; //$NON-NLS-1$//$NON-NLS-2$
203 }
204
205 /*
206 * (non-Javadoc)
207 * @see org.eclipse.linuxtools.lttng.ui.views.control.dialogs.IEnableUstEvents#isLogLevel()
208 */
209 @Override
210 public boolean isLogLevel() {
211 return fIsLogLevel;
212 }
213
214 /*
215 * (non-Javadoc)
216 * @see org.eclipse.linuxtools.lttng.ui.views.control.dialogs.IEnableUstEvents#getLogLevelType()
217 */
218 @Override
219 public LogLevelType getLogLevelType() {
220 return fLogLevelType;
221 }
222
223 /*
224 * (non-Javadoc)
225 * @see org.eclipse.linuxtools.lttng.ui.views.control.dialogs.IEnableUstEvents#getLogLevel()
226 */
227 @Override
228 public TraceLogLevel getLogLevel() {
229 return fLogLevel;
230 }
231
232 /*
233 * (non-Javadoc)
234 * @see org.eclipse.linuxtools.lttng.ui.views.control.dialogs.IEnableUstEvents#getLogLevelEventName()
235 */
236 @Override
237 public String getLogLevelEventName() {
238 return fLogLevelEventName;
239 }
240
241 // ------------------------------------------------------------------------
242 // Operations
243 // ------------------------------------------------------------------------
244
245 public void createContent() {
246
247 // Tracepoints Group
248 createTracepointsGroup();
249
250 // Wildcard Group
251 createWildCardGroup();
252
253 // Log Level Group
254 createLogLevelGroup();
255
256 // Set default enablements
257 setEnablements(GroupEnum.TRACEPOINTS);
258 }
259
260 /**
261 * Validates the kernel composite input data.
262 * @return true if configured data is valid and can be retrieved.
263 */
264 public boolean isValid() {
265
266 fIsTracepoints = fTracepointsActivateButton.getSelection();
267 fIsWildcard = fWildcardActivateButton.getSelection();
268 fIsLogLevel = fLogLevelActivateButton.getSelection();
269
270 fIsAllTracepoints = fTracepointsViewer.getChecked(fProviderGroup);
271
272 Set<String> set = new HashSet<String>();
273 Object[] checkedElements = fTracepointsViewer.getCheckedElements();
274 fSelectedEvents = new ArrayList<String>();
275 for (int i = 0; i < checkedElements.length; i++) {
276 ITraceControlComponent component = (ITraceControlComponent)checkedElements[i];
277 if (!set.contains(component.getName()) && (component instanceof BaseEventComponent)) {
278 set.add(component.getName());
279 fSelectedEvents.add(component.getName());
280 }
281 }
282
283 if (fLogLevelButton.getSelection()) {
284 fLogLevelType = LogLevelType.LOGLEVEL;
285 } else if (fLogLevelOnlyButton.getSelection()) {
286 fLogLevelType = LogLevelType.LOGLEVEL_ONLY;
287 } else {
288 fLogLevelType = LogLevelType.LOGLEVEL_NONE;
289 }
290
291 // initialize log level event name string
292 fLogLevelEventName = null;
293 String temp = fLogLevelEventNameText.getText();
294 if (!temp.matches("^[\\s]{0,}$") && !temp.matches("^[a-zA-Z0-9\\-\\_]{1,}$")) { //$NON-NLS-1$ //$NON-NLS-2$
295 MessageDialog.openError(getShell(),
296 Messages.TraceControl_EnableEventsDialogTitle,
297 Messages.TraceControl_InvalidLogLevelEventNameError + " (" + temp + ") \n"); //$NON-NLS-1$ //$NON-NLS-2$
298
299 return false;
300 }
301
302 if(!temp.matches("\\s*")) { //$NON-NLS-1$
303 fLogLevelEventName = temp;
304 }
305
306 // initialize log level event name string
307 fWildcard = null;
308 temp = fWildcardText.getText();
309 if (!temp.matches("^[\\s]{0,}$") && !temp.matches("^[a-zA-Z0-9\\-\\_\\*]{1,}$")) { //$NON-NLS-1$ //$NON-NLS-2$
310 MessageDialog.openError(getShell(),
311 Messages.TraceControl_EnableEventsDialogTitle,
312 Messages.TraceControl_InvalidWildcardError + " (" + temp + ") \n"); //$NON-NLS-1$ //$NON-NLS-2$
313
314 return false;
315 }
316
317 if(!temp.matches("\\s*")) { //$NON-NLS-1$
318 fWildcard = temp;
319 }
320
321 // validation successful -> call super.okPressed()
322 return true;
323 }
324
325 // ------------------------------------------------------------------------
326 // Helper methods
327 // ------------------------------------------------------------------------
328
329 /**
330 * Creates tracepoints group.
331 */
332 private void createTracepointsGroup() {
333 Group tpMainGroup = new Group(this, SWT.SHADOW_NONE);
334 tpMainGroup.setText(Messages.TraceControl_EnableEventsTracepointGroupName);
335 GridLayout layout = new GridLayout(2, false);
336 tpMainGroup.setLayout(layout);
337 GridData data = new GridData(GridData.FILL_BOTH);
338 tpMainGroup.setLayoutData(data);
339
340 Composite buttonComposite = new Composite(tpMainGroup, SWT.NONE);
341 layout = new GridLayout(1, true);
342 buttonComposite.setLayout(layout);
343 data = new GridData(SWT.BEGINNING, SWT.CENTER, false, true);
344 buttonComposite.setLayoutData(data);
345
346 fTracepointsActivateButton = new Button(buttonComposite, SWT.RADIO);
347 fTracepointsActivateButton.setText(Messages.TraceControl_EnableGroupSelectionName);
348 data = new GridData(GridData.FILL_HORIZONTAL);
349 fTracepointsActivateButton.setLayoutData(data);
350 fTracepointsActivateButton.addSelectionListener(new SelectionAdapter() {
351 @Override
352 public void widgetSelected(SelectionEvent e) {
353 setEnablements(GroupEnum.TRACEPOINTS);
354 }
355 });
356
357 Group tpGroup = new Group(tpMainGroup, SWT.SHADOW_NONE);
358 layout = new GridLayout(1, true);
359 tpGroup.setLayout(layout);
360 data = new GridData(GridData.FILL_BOTH);
361 tpGroup.setLayoutData(data);
362
363 fTracepointsViewer = new CheckboxTreeViewer(tpGroup, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL);
364 fTracepointsViewer.getTree().setToolTipText(Messages.TraceControl_EnableEventsTracepointTreeTooltip);
365 fTracepointsViewer.setContentProvider(new UstContentProvider());
366
367 fTracepointsViewer.setLabelProvider(new UstLabelProvider());
368 fTracepointsViewer.addCheckStateListener(new UstCheckStateListener());
369
370 fTracepointsViewer.setInput(fProviderGroup.getParent());
371 fTracepointsViewer.getTree().setLayoutData(new GridData(GridData.FILL_BOTH));
372 }
373
374 /**
375 * Creates wildcard group.
376 */
377 private void createWildCardGroup() {
378 Group wildcardMainGroup = new Group(this, SWT.SHADOW_NONE);
379 wildcardMainGroup.setText(Messages.TraceControl_EnableEventsWildcardGroupName);
380 GridLayout layout = new GridLayout(2, false);
381 wildcardMainGroup.setLayout(layout);
382 GridData data = new GridData(GridData.FILL_HORIZONTAL);
383 wildcardMainGroup.setLayoutData(data);
384
385 Composite buttonComposite = new Composite(wildcardMainGroup, SWT.NONE);
386 layout = new GridLayout(1, false);
387 buttonComposite.setLayout(layout);
388 data = new GridData(SWT.BEGINNING, SWT.CENTER, false, true);
389 buttonComposite.setLayoutData(data);
390
391 fWildcardActivateButton = new Button(buttonComposite, SWT.RADIO);
392 fWildcardActivateButton.setText(Messages.TraceControl_EnableGroupSelectionName);
393 fWildcardActivateButton.setSelection(false);
394 data = new GridData(GridData.FILL_HORIZONTAL);
395 fWildcardActivateButton.setLayoutData(data);
396 fWildcardActivateButton.addSelectionListener(new SelectionAdapter() {
397 @Override
398 public void widgetSelected(SelectionEvent e) {
399 setEnablements(GroupEnum.WILDCARD);
400 }
401 });
402
403 Group wildcardGroup = new Group(wildcardMainGroup, SWT.SHADOW_NONE);
404 layout = new GridLayout(3, true);
405 wildcardGroup.setLayout(layout);
406 data = new GridData(GridData.FILL_HORIZONTAL);
407 wildcardGroup.setLayoutData(data);
408
409 Label wildcardLabel = new Label(wildcardGroup, SWT.LEFT);
410 wildcardLabel.setText(Messages.TraceControl_EnableEventsWildcardLabel);
411 data = new GridData(GridData.FILL_HORIZONTAL);
412 data.horizontalSpan = 1;
413 wildcardLabel.setLayoutData(data);
414
415 fWildcardText = new Text(wildcardGroup, SWT.LEFT);
416 fWildcardText.setToolTipText(Messages.TraceControl_EnableEventsWildcardTooltip);
417 data = new GridData(GridData.FILL_HORIZONTAL);
418 data.horizontalSpan = 2;
419 fWildcardText.setLayoutData(data);
420 }
421
422 /**
423 * Creates log level group.
424 */
425 private void createLogLevelGroup() {
426 Group logLevelMainGroup = new Group(this, SWT.SHADOW_NONE);
427 logLevelMainGroup.setText(Messages.TraceControl_EnableEventsLogLevelGroupName);
428 GridLayout layout = new GridLayout(2, false);
429 logLevelMainGroup.setLayout(layout);
430 GridData data = new GridData(GridData.FILL_HORIZONTAL);
431 logLevelMainGroup.setLayoutData(data);
432
433 Composite buttonComposite = new Composite(logLevelMainGroup, SWT.NONE);
434 layout = new GridLayout(1, false);
435 buttonComposite.setLayout(layout);
436 data = new GridData(SWT.BEGINNING, SWT.CENTER, false, true);
437 buttonComposite.setLayoutData(data);
438
439 fLogLevelActivateButton = new Button(buttonComposite, SWT.RADIO);
440 fLogLevelActivateButton.setText(Messages.TraceControl_EnableGroupSelectionName);
441 fLogLevelActivateButton.setSelection(false);
442 data = new GridData(GridData.FILL_HORIZONTAL);
443 fLogLevelActivateButton.setLayoutData(data);
444 fLogLevelActivateButton.addSelectionListener(new SelectionAdapter() {
445 @Override
446 public void widgetSelected(SelectionEvent e) {
447 setEnablements(GroupEnum.LOGLEVEL);
448 }
449 });
450
451 Group logLevelGroup = new Group(logLevelMainGroup, SWT.SHADOW_NONE);
452 layout = new GridLayout(3, true);
453 logLevelGroup.setLayout(layout);
454 logLevelGroup.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
455
456 Label logLevelEventNameLabel = new Label(logLevelGroup, SWT.LEFT);
457 logLevelEventNameLabel.setText(Messages.TraceControl_EnableEventsEventNameLabel);
458
459 data = new GridData(GridData.FILL_BOTH);
460 data.horizontalSpan = 1;
461 logLevelEventNameLabel.setLayoutData(data);
462
463 fLogLevelEventNameText = new Text(logLevelGroup, SWT.LEFT);
464 fLogLevelEventNameText.setToolTipText(Messages.TraceControl_EnableEventsLoglevelEventNameTooltip);
465 data = new GridData(GridData.FILL_BOTH);
466 data.horizontalSpan = 2;
467 fLogLevelEventNameText.setLayoutData(data);
468
469 TraceLogLevel[] levels = TraceLogLevel.values();
470
471 String[] levelNames = new String[levels.length - 1];
472 int k = 0;
473 for (int i = 0; i < levels.length; i++) {
474 if (levels[i] != TraceLogLevel.LEVEL_UNKNOWN) {
475 levelNames[k++] = levels[i].getInName();
476 }
477 }
478
479 fLogLevelCombo = new CCombo(logLevelGroup, SWT.READ_ONLY);
480 fLogLevelCombo.setItems(levelNames);
481 fLogLevelCombo.setToolTipText(Messages.TraceControl_EnableEventsLogLevelTooltip);
482 data = new GridData(GridData.FILL_HORIZONTAL);
483 data.horizontalSpan = 4;
484 fLogLevelCombo.setLayoutData(data);
485
486 fLogLevelButton = new Button(logLevelGroup, SWT.RADIO);
487 fLogLevelButton.setText(Messages.TraceControl_EnableEventsLogLevelTypeName);
488 fLogLevelButton.setToolTipText(Messages.TraceControl_EnableEventsLogLevelTypeTooltip);
489 data = new GridData(GridData.FILL_BOTH);
490 fLogLevelButton.setLayoutData(data);
491
492 fLogLevelOnlyButton = new Button(logLevelGroup, SWT.RADIO);
493 fLogLevelOnlyButton.setText(Messages.TraceControl_EnableEventsLogLevelOnlyTypeName);
494 fLogLevelOnlyButton.setToolTipText(Messages.TraceControl_EnableEventsLogLevelOnlyTypeTooltip);
495 data = new GridData(GridData.FILL_BOTH);
496 fLogLevelButton.setLayoutData(data);
497 }
498
499 /**
500 * Enable/selects widgets depending on the group specified.
501 * @param group - group to enable.
502 */
503 private void setEnablements(GroupEnum group) {
504
505 // Enable/disable trace point items
506 fTracepointsActivateButton.setSelection(group == GroupEnum.TRACEPOINTS);
507 fTracepointsViewer.getTree().setEnabled(group == GroupEnum.TRACEPOINTS);
508
509 // Enable/disable wildcard items
510 fWildcardActivateButton.setSelection(group == GroupEnum.WILDCARD);
511 fWildcardText.setEnabled(group == GroupEnum.WILDCARD);
512
513 // Enable/disable log level items
514 fLogLevelActivateButton.setSelection(group == GroupEnum.LOGLEVEL);
515 fLogLevelEventNameText.setEnabled(group == GroupEnum.LOGLEVEL);
516 fLogLevelCombo.setEnabled(group == GroupEnum.LOGLEVEL);
517 fLogLevelButton.setEnabled(group == GroupEnum.LOGLEVEL);
518 fLogLevelOnlyButton.setEnabled(group == GroupEnum.LOGLEVEL);
519 }
520
521 // ------------------------------------------------------------------------
522 // Local classes
523 // ------------------------------------------------------------------------
524 /**
525 * Content provider for the tracepoints tree.
526 */
527 final public class UstContentProvider extends TraceControlContentProvider {
528 @Override
529 public Object[] getChildren(Object parentElement) {
530 if (parentElement instanceof TargetNodeComponent) {
531 List<ITraceControlComponent> children = ((ITraceControlComponent)parentElement).getChildren(TraceProviderGroup.class);
532 return (ITraceControlComponent[]) children.toArray(new ITraceControlComponent[children.size()]);
533 }
534 if (parentElement instanceof TraceProviderGroup) {
535 List<ITraceControlComponent> children = ((ITraceControlComponent)parentElement).getChildren(UstProviderComponent.class);
536 return (ITraceControlComponent[]) children.toArray(new ITraceControlComponent[children.size()]);
537 }
538 if (parentElement instanceof ITraceControlComponent) {
539 return ((ITraceControlComponent)parentElement).getChildren();
540 }
541 return new Object[0];
542 }
543 }
544
545 /**
546 * Content label for the tracepoints tree.
547 */
548 final public class UstLabelProvider extends TraceControlLabelProvider {
549 @Override
550 public Image getImage(Object element) {
551 return null;
552 }
553 @Override
554 public String getText(Object element) {
555 if ((element != null) && (element instanceof TraceProviderGroup)) {
556 return Messages.TraceControl_EnableEventsTracepointTreeAllLabel;
557 }
558
559 if ((element != null) && (element instanceof UstProviderComponent)) {
560 return Messages.TraceControl_EnableEventsTracepointTreeAllLabel + " - " + ((UstProviderComponent)element).getName(); //$NON-NLS-1$
561 }
562 return super.getText(element);
563 }
564 }
565
566 /**
567 * Check state listener for the tracepoints tree.
568 */
569 final public class UstCheckStateListener implements ICheckStateListener {
570 @Override
571 public void checkStateChanged(CheckStateChangedEvent event) {
572 if (event.getChecked()) {
573 if (event.getElement() instanceof TraceProviderGroup) {
574 fTracepointsViewer.setSubtreeChecked(event.getElement(), true);
575 }
576 if (event.getElement() instanceof UstProviderComponent) {
577 fTracepointsViewer.setSubtreeChecked(event.getElement(), true);
578 }
579 } else {
580 if (event.getElement() instanceof TraceProviderGroup) {
581 fTracepointsViewer.setSubtreeChecked(event.getElement(), true);
582 }
583 if (event.getElement() instanceof UstProviderComponent) {
584 ITraceControlComponent component = (ITraceControlComponent) event.getElement();
585 fTracepointsViewer.setSubtreeChecked(event.getElement(), false);
586 fTracepointsViewer.setChecked(component.getParent(), false);
587 } else {
588 ITraceControlComponent component = (ITraceControlComponent) event.getElement();
589 fTracepointsViewer.setChecked(component.getParent(), false);
590 fTracepointsViewer.setChecked(component.getParent().getParent(), false);
591 }
592 }
593 }
594 }
595 }
This page took 0.085769 seconds and 5 git commands to generate.