Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Pazderski2019-06-04 16:56:22 +0000
committerEric Williams2019-06-10 15:51:56 +0000
commita4938743e4a8061dce698d5f6c3850bb2a4930c7 (patch)
tree06cc6ba776695aaeff3114a02f4b51a2716001f4 /examples
parent55635f432dd8824cb107c035a00fa4435bbf93df (diff)
downloadeclipse.platform.swt-a4938743e4a8061dce698d5f6c3850bb2a4930c7.tar.gz
eclipse.platform.swt-a4938743e4a8061dce698d5f6c3850bb2a4930c7.tar.xz
eclipse.platform.swt-a4938743e4a8061dce698d5f6c3850bb2a4930c7.zip
Bug 547934 - [Snippets] Add SnippetExplorer to filter and launch SWT
Snippets Change-Id: I47d96bf18e94b8c318e3f00a32b5af24a452989a Signed-off-by: Paul Pazderski <paul-eclipse@ppazderski.de>
Diffstat (limited to 'examples')
-rw-r--r--examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetExplorer.java1229
-rw-r--r--examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetLauncher.java11
-rw-r--r--examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetsConfig.java41
3 files changed, 1275 insertions, 6 deletions
diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetExplorer.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetExplorer.java
new file mode 100644
index 0000000000..f3960b28cb
--- /dev/null
+++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetExplorer.java
@@ -0,0 +1,1229 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Paul Pazderski and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Paul Pazderski - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.snippets;
+
+import java.io.*;
+import java.lang.ProcessBuilder.*;
+import java.lang.reflect.*;
+import java.nio.charset.*;
+import java.nio.file.*;
+import java.nio.file.Path;
+import java.util.*;
+import java.util.List;
+import java.util.concurrent.*;
+import java.util.regex.*;
+import java.util.regex.Pattern;
+
+import org.eclipse.swt.*;
+import org.eclipse.swt.custom.*;
+import org.eclipse.swt.graphics.*;
+import org.eclipse.swt.layout.*;
+import org.eclipse.swt.program.*;
+import org.eclipse.swt.widgets.*;
+
+/**
+ * A useful application to list, filter and run the available Snippets.
+ */
+public class SnippetExplorer {
+
+ private static final String USAGE_EXPLANATION = "Welcome to the SnippetExplorer!\n"
+ + "\n"
+ + "This tool will help you to explore and test the large collection of SWT example Snippets. "
+ + "You can use the text field on top to filter the Snippets by there description or Snippet number. "
+ + "To start a Snippet you can either double click its entry, press enter or use the button below. "
+ + "It is also possible to start multiple Snippets at once. (exact behavior depends on selected Snippet-Runner)\n"
+ + "\n"
+ + "It is recommended to start the Snippet Explorer connected to a console since some of the Snippets "
+ + "print useful informations to the console or do not open a window at all.\n"
+ + "\n"
+ + "The Explorer supports (dependent on your OS and environment) different modes to start Snippets. Those runners are:\n"
+ + "\n"
+ + " \u2022 Thread Runner: Snippets are executed as threads of the Explorer.\n"
+ + "\t- This runner is only available if the environment supports multiple Displays at the same time. (only Windows at the moment)\n"
+ + "\t- Multiple Snippets can be run parallel using this runner.\n"
+ + "\t- All running Snippets are closed when the explorer exits.\n"
+ + "\t- If to many Snippets are run in parallel SWT may run out of handles.\n"
+ + "\t- If a Snippet calls System.exit it will also force the explorer itself and all other running Snippets to exit as well.\n"
+ + "\n"
+ + " \u2022 Process Runner: Snippets are executed as separate processes.\n"
+ + "\t- This runner is only available if a JRE was found which can be used to start the Snippets.\n"
+ + "\t- Multiple Snippets can be run parallel using this runner.\n"
+ + "\t- This runner is more likely to fail Snippet launch due to incomplete classpath or other launch problems.\n"
+ + "\t- When the explorer exits it try to close all running Snippets but has less control over it as the Thread runner.\n"
+ + "\t- Unlike the Thread runner the Process runner is resisted to faulty Snippets. (e.g. Snippets calling System.exit)\n"
+ + "\n"
+ + " \u2022 Serial Runner: Snippets are executed one after another instead of the explorer.\n"
+ + "\t- This runner is always available.\n"
+ + "\t- Cannot run Snippets parallel.\n"
+ + "\t- To run Snippets the explorer gets closed, executes the selected Snippets one after another in the same JVM "
+ + "and after the last Snippet has finished restarts the Snippet Explorer.\n"
+ + "\t- A Snippet calling System.exit will stop the Snippet chain and the explorer itself can not restart.";
+
+ /** Max length for Snippet description in the main table. */
+ private static final int MAX_DESCRIPTION_LENGTH_IN_TABLE = 80;
+ /**
+ * If the user tries to start more than this number of Snippets at once a
+ * warning message is shown.
+ */
+ private static final int START_MANY_SNIPPETS_WARNING_THREASHOLD = 10;
+ /** Message shown in the filter text field if empty. */
+ private static final String FILTER_HINT = "type to filter list";
+ /**
+ * Delay in milliseconds before a changed filter value is applied on the list.
+ */
+ private static final int FILTER_DELAY_MS = 200;
+ /**
+ * Time snippets get to stop before tried to be killed forcefully. (currently
+ * applied per runner)
+ */
+ private static final int SHUTDOWN_GRACE_TIME_MS = 5000;
+ /** Link to online snippet source. Used if no local source is available. */
+ private static final String SNIPPET_SOURCE_LINK_TEMPLATE = "https://git.eclipse.org/c/platform/"
+ + "eclipse.platform.swt.git/tree/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/%s.java";
+
+ /**
+ * Whether or not SWT support creating of multiple {@link Display} instances on
+ * the current system. Required to use the thread runner mode.
+ */
+ private static boolean multiDisplaySupport;
+ /**
+ * The command used to invoke the java binary. May be <code>null</code> if not
+ * found. Required to use the process runner mode.
+ */
+ private static String javaCommand;
+ /** The list of available Snippets. */
+ private static List<Snippet> snippets;
+
+ /** The runner used for thread mode. */
+ private final SnippetRunner THREAD_RUNNER = new SnippetRunnerThread();
+ /** The runner used for process mode. */
+ private final SnippetRunner PROCESS_RUNNER = new SnippetRunnerProcess();
+
+ private Display display;
+ private Shell shell;
+
+ /** Helper to perform the delayed list update if filter changed. */
+ private ListUpdater listUpdater;
+ /** Text field to filter Snippet list. */
+ private Text filterField;
+ /** The main table listing available Snippets. */
+ private Table snippetTable;
+ /** Button to run selected Snippets. */
+ private Button startSelectedButton;
+ /** Snippet runner selection. */
+ private Combo runnerCombo;
+ /** The tabfolder to show information for selected Snippet. */
+ private TabFolder infoTabs;
+ /** Element to show Snippet description or general help. */
+ private StyledText descriptionView;
+ /**
+ * Element to show Snippets source code or link to source if not local
+ * available.
+ */
+ private StyledText sourceView;
+ /** Element to show Snippet preview if possible. */
+ private Label previewImageLabel;
+
+ /**
+ * The snippet runner used for next snippet start. If <code>null</code> Snippets
+ * are run serial.
+ */
+ private SnippetRunner snippetRunner;
+ /** Used to map {@link #runnerCombo} selection to actual Snippet runner. */
+ private List<SnippetRunner> runnerMapping = new ArrayList<>();
+ /** The Snippet currently shown in {@link #infoTabs}. May be <code>null</code>. */
+ private Snippet currentInfoSnippet = null;
+ /** Snippets currently run in serial runner mode. May be <code>null</code>. */
+ private List<Snippet> serialSnippets;
+ /**
+ * The SnippetExplorer location for the next {@link Shell#open()}. Used for
+ * restart after serial runner finished.
+ */
+ private Point nextExplorerLocation = null;
+
+ /**
+ * SnippetExplorer main method.
+ *
+ * @param args does not parse any arguments
+ */
+ public static void main(String[] args) throws Exception {
+ final String os = System.getProperty("os.name");
+ multiDisplaySupport = (os != null && os.toLowerCase().contains("windows"));
+ if (canRunCommand("java")) {
+ javaCommand = "java";
+ } else {
+ final String javaHome = System.getProperty("java.home");
+ if (javaHome != null) {
+ final Path java = Paths.get(javaHome, "bin", "java");
+ java.normalize();
+ if (canRunCommand(java.toString())) {
+ javaCommand = java.toString();
+ }
+ }
+ }
+
+ snippets = loadSnippets();
+ snippets.sort((a, b) -> {
+ int cmp = Integer.compare(a.snippetNum, b.snippetNum);
+ if (cmp == 0) {
+ cmp = a.snippetName.compareTo(b.snippetName);
+ }
+ return cmp;
+ });
+
+ new SnippetExplorer().open();
+ }
+
+ /**
+ * Test if the given command can be executed.
+ *
+ * @param command command to test
+ * @return <code>false</code> if executing the command failed for any reason
+ */
+ private static boolean canRunCommand(String command) {
+ try {
+ final Process p = Runtime.getRuntime().exec(command);
+ p.waitFor(150, TimeUnit.MILLISECONDS);
+ if (p.isAlive()) {
+ p.destroy();
+ p.waitFor(100, TimeUnit.MILLISECONDS);
+ if (p.isAlive()) {
+ p.destroyForcibly();
+ }
+ }
+ return true;
+ } catch (Exception ex) {
+ return false;
+ }
+ }
+
+ public SnippetExplorer() {
+ }
+
+ /**
+ * Initializes and shows the SnippetExplorer. The method doesn't return until
+ * the explorer is closed or otherwise disposed.
+ */
+ public void open() {
+ initialize();
+ runEventLoop();
+ }
+
+ /**
+ * Initialize the SnippetExplorer. Can be called again if the current explorer
+ * was properly disposed.
+ */
+ private void initialize() {
+ display = Display.getDefault();
+ snippetRunner = null;
+ shell = new Shell(display);
+ if (nextExplorerLocation != null) {
+ shell.setLocation(nextExplorerLocation);
+ }
+ shell.setText("SWT Snippet Explorer");
+
+ createControls(shell);
+
+ final String[] columns = new String[] { "Name", "Description" };
+ for (String col : columns) {
+ final TableColumn tableCol = new TableColumn(snippetTable, SWT.NONE);
+ tableCol.setText(col);
+ tableCol.setToolTipText(col);
+ tableCol.setResizable(true);
+ tableCol.setMoveable(true);
+ }
+ updateTable(null);
+
+ for (TableColumn col : snippetTable.getColumns()) {
+ col.pack();
+ }
+ final GridData rightSideLayout = (GridData) infoTabs.getLayoutData();
+ final Point tableSize = snippetTable.getSize();
+ rightSideLayout.widthHint = tableSize.x;
+ rightSideLayout.heightHint = tableSize.y;
+ shell.pack();
+ shell.open();
+ }
+
+ /** Initialize the SnippetExplorer controls.
+ *
+ * @param shell parent shell
+ */
+ private void createControls(Shell shell) {
+ shell.setLayout(new FormLayout());
+
+ if (listUpdater == null) {
+ listUpdater = new ListUpdater();
+ listUpdater.start();
+ }
+
+ final Composite leftContainer = new Composite(shell, SWT.NONE);
+ leftContainer.setLayout(new GridLayout());
+
+ final Sash splitter = new Sash(shell, SWT.BORDER | SWT.VERTICAL);
+ final int splitterWidth = 3;
+ splitter.addListener(SWT.Selection, e -> splitter.setBounds(e.x, e.y, e.width, e.height));
+
+ final Composite rightContainer = new Composite(shell, SWT.NONE);
+ rightContainer.setLayout(new GridLayout());
+
+ FormData formData = new FormData();
+ formData.left = new FormAttachment(0, 0);
+ formData.right = new FormAttachment(splitter, 0);
+ formData.top = new FormAttachment(0, 0);
+ formData.bottom = new FormAttachment(100, 0);
+ leftContainer.setLayoutData(formData);
+
+ formData = new FormData();
+ formData.left = new FormAttachment(50, 0);
+ formData.right = new FormAttachment(50, splitterWidth);
+ formData.top = new FormAttachment(0, 0);
+ formData.bottom = new FormAttachment(100, 0);
+ splitter.setLayoutData(formData);
+ splitter.addListener(SWT.Selection, event -> {
+ final FormData splitterFormData = (FormData) splitter.getLayoutData();
+ splitterFormData.left = new FormAttachment(0, event.x);
+ splitterFormData.right = new FormAttachment(0, event.x + splitterWidth);
+ shell.layout();
+ });
+
+ formData = new FormData();
+ formData.left = new FormAttachment(splitter, 0);
+ formData.right = new FormAttachment(100, 0);
+ formData.top = new FormAttachment(0, 0);
+ formData.bottom = new FormAttachment(100, 0);
+ rightContainer.setLayoutData(formData);
+
+ filterField = new Text(leftContainer, SWT.SINGLE | SWT.BORDER | SWT.SEARCH | SWT.ICON_CANCEL);
+ filterField.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+ filterField.setMessage(FILTER_HINT);
+ filterField.addListener(SWT.Modify, event -> {
+ listUpdater.updateInMs(FILTER_DELAY_MS);
+ });
+ snippetTable = new Table(leftContainer, SWT.MULTI | SWT.BORDER | SWT.FULL_SELECTION);
+ snippetTable.setLinesVisible(true);
+ snippetTable.setHeaderVisible(true);
+ final GridData data = new GridData(SWT.FILL, SWT.FILL, true, true);
+ data.heightHint = 500;
+ snippetTable.setLayoutData(data);
+ snippetTable.addListener(SWT.MouseDoubleClick, event -> {
+ final Point clickPoint = new Point(event.x, event.y);
+ launchSnippet(snippetTable.getItem(clickPoint));
+ });
+ snippetTable.addListener(SWT.KeyUp, event -> {
+ if (event.keyCode == '\r' || event.keyCode == '\n') {
+ launchSnippet(snippetTable.getSelection());
+ }
+ });
+
+ final Composite buttonRow = new Composite(leftContainer, SWT.NONE);
+ buttonRow.setLayout(new GridLayout(3, false));
+ buttonRow.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false));
+
+ startSelectedButton = new Button(buttonRow, SWT.LEAD);
+ startSelectedButton.setText(" Start &selected Snippets");
+ snippetTable.addListener(SWT.Selection, event -> {
+ startSelectedButton.setEnabled(snippetTable.getSelectionCount() > 0);
+ updateInfoTab(snippetTable.getSelection());
+ });
+ startSelectedButton.setEnabled(snippetTable.getSelectionCount() > 0);
+ startSelectedButton.addListener(SWT.Selection, event -> {
+ launchSnippet(snippetTable.getSelection());
+ });
+
+ final Label runnerLabel = new Label(buttonRow, SWT.NONE);
+ runnerLabel.setText("Snippet Runner:");
+ runnerLabel.setLayoutData(new GridData(SWT.TRAIL, SWT.CENTER, true, false));
+
+ runnerCombo = new Combo(buttonRow, SWT.TRAIL | SWT.DROP_DOWN | SWT.READ_ONLY);
+ runnerMapping.clear();
+ if (multiDisplaySupport) {
+ runnerCombo.add("Thread");
+ runnerMapping.add(THREAD_RUNNER);
+ }
+ if (javaCommand != null) {
+ runnerCombo.add("Process");
+ runnerMapping.add(PROCESS_RUNNER);
+ }
+ runnerCombo.add("Serial");
+ runnerMapping.add(null);
+ runnerCombo.setData(runnerMapping);
+ runnerCombo.addListener(SWT.Modify, event -> {
+ if (runnerMapping.size() > runnerCombo.getSelectionIndex()) {
+ snippetRunner = runnerMapping.get(runnerCombo.getSelectionIndex());
+ } else {
+ System.err.println("Unknown runner index " + runnerCombo.getSelectionIndex());
+ }
+ });
+ runnerCombo.select(0);
+
+ infoTabs = new TabFolder(rightContainer, SWT.TOP);
+ infoTabs.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ descriptionView = new StyledText(infoTabs, SWT.MULTI | SWT.WRAP | SWT.READ_ONLY | SWT.V_SCROLL);
+
+ sourceView = new StyledText(infoTabs, SWT.MULTI | SWT.READ_ONLY | SWT.V_SCROLL | SWT.H_SCROLL);
+ setMonospaceFont(sourceView);
+
+ final ScrolledComposite previewContainer = new ScrolledComposite(infoTabs, SWT.V_SCROLL | SWT.H_SCROLL);
+ previewImageLabel = new Label(previewContainer, SWT.NONE);
+ previewContainer.setContent(previewImageLabel);
+
+ final TabItem descriptionTab = new TabItem(infoTabs, SWT.NONE);
+ descriptionTab.setText("Description");
+ descriptionTab.setControl(descriptionView);
+ final TabItem sourceTab = new TabItem(infoTabs, SWT.NONE);
+ sourceTab.setText("Source");
+ sourceTab.setControl(sourceView);
+ final TabItem previewTab = new TabItem(infoTabs, SWT.NONE);
+ previewTab.setText("Preview");
+ previewTab.setControl(previewContainer);
+
+ updateInfoTab(null, true);
+ updateInfoTab(snippetTable.getSelection());
+ }
+
+ /**
+ * Try to set a monospace font for this control. Other font properties like
+ * fontSize remain unchanged.
+ *
+ * @param control control to modify
+ */
+ private void setMonospaceFont(Control control) {
+ final FontData[] fontData = control.getFont().getFontData();
+ Font font = null;
+ if (font == null) {
+ font = tryCreateFont("Consolas", fontData);
+ }
+ if (font == null) {
+ font = tryCreateFont("DejaVu Sans Mono", fontData);
+ }
+ if (font == null) {
+ font = tryCreateFont("Noto Mono", fontData);
+ }
+ if (font == null) {
+ font = tryCreateFont("Liberation Mono", fontData);
+ }
+ if (font == null) {
+ font = tryCreateFont("Ubuntu Mono", fontData);
+ }
+ if (font == null) {
+ font = tryCreateFont("Courier New", fontData);
+ }
+ if (font == null) {
+ font = tryCreateFont("Courier", fontData);
+ }
+ if (font == null) {
+ font = tryCreateFont("Monospace", fontData);
+ }
+
+ if (font != null) {
+ control.setFont(font);
+ }
+ }
+
+ /**
+ * Try to create font with given name based on existing {@link FontData}.
+ *
+ * @param fontName name of font to create
+ * @param existingData existing font data to be used for other font attributes
+ * @return the created font or <code>null</code> if failed (e.g. no font
+ * available with given name)
+ */
+ private Font tryCreateFont(String fontName, FontData[] existingData) {
+ for (int i = 0; i < existingData.length; i++) {
+ existingData[i].setName(fontName);
+ }
+ try {
+ return new Font(display, existingData);
+ } catch (SWTException e) {
+ return null;
+ }
+ }
+
+ /**
+ * Update the info tabs for the given items. The behavior may change. At the
+ * moment informations are only shown for single items.
+ *
+ * @param items items to show info for
+ */
+ private void updateInfoTab(TableItem[] items) {
+ // if multiple snippets are selected no info are shown
+ if (items.length != 1) {
+ updateInfoTab((TableItem) null, false);
+ } else {
+ updateInfoTab(items[0], false);
+ }
+ }
+
+ /**
+ * Update the info tabs (right side of the explorer) for the given item.
+ *
+ * @param item the selected item containing Snippet metadata (may be
+ * <code>null</code>)
+ * @param force the tabs are only updated if they not already show info for the
+ * given item. If this is <code>true</code> the tabs are updated
+ * anyway.
+ */
+ private void updateInfoTab(TableItem item, boolean force) {
+ final Snippet snippet = (item != null && item.getData() instanceof Snippet) ? (Snippet) item.getData() : null;
+ if (!force && currentInfoSnippet == snippet) {
+ return;
+ }
+ if (snippet == null) {
+ descriptionView.setText(USAGE_EXPLANATION);
+ sourceView.setText("");
+ updatePreviewImage(null, "");
+ } else {
+ descriptionView.setText(snippet.snippetName + "\n\n" + snippet.description);
+ if (snippet.source == null) {
+ sourceView.setWordWrap(true);
+ final String msg = "No source available for " + snippet.snippetName + " but you may find it at:\n\n";
+ final String link = String.format(Locale.ROOT, SNIPPET_SOURCE_LINK_TEMPLATE, snippet.snippetName);
+ sourceView.setText(msg + link);
+
+ final StyleRange linkStyle = new StyleRange();
+ linkStyle.start = msg.length();
+ linkStyle.length = link.length();
+ linkStyle.underline = true;
+ linkStyle.underlineStyle = SWT.UNDERLINE_LINK;
+ sourceView.setStyleRange(linkStyle);
+
+ sourceView.addListener(SWT.MouseDown, event -> {
+ int offset = sourceView.getOffsetAtPoint(new Point(event.x, event.y));
+ if (offset != -1) {
+ try {
+ final StyleRange style = sourceView.getStyleRangeAtOffset(offset);
+ if (style != null && style.underline && style.underlineStyle == SWT.UNDERLINE_LINK) {
+ Program.launch(link);
+ }
+ } catch (IllegalArgumentException e) {
+ // no character under event.x, event.y
+ }
+ }
+ });
+ } else {
+ sourceView.setWordWrap(false);
+ sourceView.setText(snippet.source);
+ }
+ try {
+ final Image previewImage = getPreviewImage(snippet);
+ updatePreviewImage(previewImage, previewImage == null ? "No preview image available." : "");
+ } catch (IOException e) {
+ updatePreviewImage(null, "Failed to load preview image: " + e);
+ }
+ }
+ currentInfoSnippet = snippet;
+ }
+
+ /**
+ * Update the control showing the image. If <em>image</em> is <code>null</code>
+ * show the <em>text</em> instead.
+ *
+ * @param image the image to show
+ * @param text the alternative text to show if image is <code>null</code>
+ */
+ private void updatePreviewImage(Image image, String text) {
+ final Image previousImage = previewImageLabel.getImage();
+ previewImageLabel.setImage(image);
+ if (image == null && text != null) {
+ previewImageLabel.setText(text);
+ }
+ if (previousImage != null) {
+ previousImage.dispose();
+ }
+ previewImageLabel.pack(true);
+ }
+
+ /**
+ * Get the preview image for the Snippet.
+ *
+ * @param snippet Snippet's metadata to load preview image for
+ * @return the preview image or <code>null</code> if none available
+ * @throws IOException if image loading failed
+ */
+ private Image getPreviewImage(Snippet snippet) throws IOException {
+ final Path previewFile = Paths.get("previews", snippet.snippetName + ".png");
+ if (Files.exists(previewFile)) {
+ try (InputStream imageStream = Files.newInputStream(previewFile)) {
+ return new Image(display, imageStream);
+ }
+ }
+ try (InputStream imageStream = SnippetExplorer.class
+ .getResourceAsStream("/previews/" + snippet.snippetName + ".png")) {
+ if (imageStream != null) {
+ return new Image(display, imageStream);
+ }
+ }
+ try (InputStream imageStream = ClassLoader
+ .getSystemResourceAsStream("previews/" + snippet.snippetName + ".png")) {
+ if (imageStream != null) {
+ return new Image(display, imageStream);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Load all available Snippets from the preconfigured source path and from the
+ * current classppath.
+ *
+ * @return all found Snippets (never <code>null</code>)
+ */
+ private static List<Snippet> loadSnippets() {
+ // Similar to SnippetLauncher this explorer tries to load Snippet0 to Snippet500
+ // even if no sources are available. This array is used to track which snippets
+ // are already loaded from source.
+ final boolean[] loadedSnippets = new boolean[501];
+ final List<Snippet> snippets = new ArrayList<>();
+
+ // load snippets from source directory
+ final Path sourceDir = SnippetsConfig.SNIPPETS_SOURCE_DIR.toPath();
+ if (Files.exists(sourceDir)) {
+ try (DirectoryStream<Path> files = Files.newDirectoryStream(sourceDir, "*.java")) {
+ for (Path file : files) {
+ try {
+ final Snippet snippet = snippetFromSource(file);
+ if (snippet == null) {
+ continue;
+ }
+ snippets.add(snippet);
+ if (snippet.snippetNum >= 0) {
+ loadedSnippets[snippet.snippetNum] = true;
+ }
+ } catch (ClassNotFoundException | IOException ex) {
+ System.err.println("Failed to load snippet from " + file + ". Error: " + ex);
+ }
+ }
+ } catch (IOException ex) {
+ System.err.println("Failed to access source directory " + sourceDir + ". Error: " + ex);
+ }
+ }
+
+ // load snippets from classpath
+ for (int i = 0; i < loadedSnippets.length; i++) {
+ if (!loadedSnippets[i]) {
+ final int snippetNum = i;
+ final String snippetName = "Snippet" + snippetNum;
+ final Class<?> snippetClass;
+ try {
+ snippetClass = Class.forName(SnippetsConfig.SNIPPETS_PACKAGE + "." + snippetName, false,
+ SnippetExplorer.class.getClassLoader());
+ } catch (ClassNotFoundException e) {
+ continue;
+ }
+ final String[] arguments = SnippetsConfig.getSnippetArguments(snippetNum);
+ snippets.add(new Snippet(snippetNum, snippetName, snippetClass, null, null, arguments));
+ }
+ }
+
+ return snippets;
+ }
+
+ /**
+ * Load Snippet metadata from the Java source file found at the given path.
+ *
+ * @param sourceFile the source file to load
+ * @return the gathered Snippet metadata or <code>null</code> if failed
+ * @throws IOException on errors loading the source file
+ * @throws ClassNotFoundException if loading the Snippets corresponding class
+ * file failed
+ */
+ private static Snippet snippetFromSource(Path sourceFile) throws IOException, ClassNotFoundException {
+ final Pattern snippetNamePattern = Pattern.compile("Snippet([0-9]+)", Pattern.CASE_INSENSITIVE);
+ sourceFile = sourceFile.normalize();
+ final String filename = sourceFile.getFileName().toString();
+ final String snippetName = filename.substring(0, filename.lastIndexOf('.'));
+ final Class<?> snippeClass = Class.forName(SnippetsConfig.SNIPPETS_PACKAGE + "." + snippetName, false,
+ SnippetExplorer.class.getClassLoader());
+ int snippetNum = Integer.MIN_VALUE;
+ final Matcher snippetNameMatcher = snippetNamePattern.matcher(snippetName);
+ if (snippetNameMatcher.matches()) {
+ try {
+ snippetNum = Integer.parseInt(snippetNameMatcher.group(1), 10);
+ } catch (NumberFormatException e) {
+ }
+ }
+
+ // do not load snippets without number yet
+ if (snippetNum < 0) {
+ return null;
+ }
+
+ final String src = getSnippetSource(sourceFile);
+ final String description = extractSnippetDescription(src);
+ final String[] arguments = SnippetsConfig.getSnippetArguments(snippetNum);
+ return new Snippet(snippetNum, snippetName, snippeClass, src, description, arguments);
+ }
+
+ /**
+ * Read the content of the source file. (expect <code>UTF-8</code> encoding)
+ *
+ * @param sourceFile source file to load
+ * @return the files content or <code>null</code> if file does not exist
+ * @throws IOException if loading failed
+ */
+ private static String getSnippetSource(Path sourceFile) throws IOException {
+ if (!Files.exists(sourceFile)) {
+ return null;
+ }
+ final String src = new String(Files.readAllBytes(sourceFile), StandardCharsets.UTF_8);
+ return src;
+ }
+
+ /**
+ * Tries to extract a snippet description from the snippet source.
+ * <p>
+ * If description has multiple lines the delimiter is always in UNIX-style (\n).
+ * </p>
+ *
+ * @param snippetSrc the snippet source code
+ * @return the extracted snippet description. If none found returns
+ * <code>null</code> or in some cases an empty string.
+ */
+ private static String extractSnippetDescription(String snippetSrc) {
+ if (snippetSrc == null) {
+ return null;
+ }
+ // Usually the second block comment contains a description of the snippet
+ // therefore this method returns the first block comment not containing the
+ // usual copyright keywords.
+ // Note: currently only real block comments are considered. A bunch of line
+ // comments forming a block (like that comment you're reading right now) are
+ // ignored.
+
+ final Pattern blockCommentPattern = Pattern.compile("/\\*\\*?(.*?)\\*/", Pattern.DOTALL);
+ final Matcher blockCommentMatcher = blockCommentPattern.matcher(snippetSrc);
+ while (blockCommentMatcher.find()) {
+ String comment = blockCommentMatcher.group(1);
+ if (comment.contains("Copyright (c)") || comment.contains("https://www.eclipse.org/legal/epl-2.0/")) {
+ continue;
+ }
+ // normalize line breaks
+ comment = comment.replaceAll("\r\n?", "\n");
+ // remove '*' at line start and trim lines
+ comment = comment.replaceAll("[ \t]*\n[ \\t]*\\*+[ \\t]*", "\n");
+ // trim start and end
+ comment = comment.trim();
+ return comment;
+ }
+ return null;
+ }
+
+ private void updateTable(String filter) {
+ if (filter == null) {
+ filter = "";
+ }
+ filter = filter.toLowerCase();
+ int itemIndex = 0;
+ final int itemCount = snippetTable.getItemCount();
+ snippetTable.setRedraw(false);
+ snippetTable.deselectAll();
+ for (Snippet snippet : snippets) {
+ if (filter.isEmpty() || (snippet.description != null && snippet.description.toLowerCase().contains(filter))
+ || String.valueOf(snippet.snippetNum).equals(filter)) {
+ final TableItem item = itemIndex < itemCount ? snippetTable.getItem(itemIndex)
+ : new TableItem(snippetTable, SWT.NONE);
+ fillTableItem(item, snippet);
+ itemIndex++;
+ }
+ }
+ if (itemIndex < itemCount) {
+ snippetTable.remove(itemIndex, itemCount - 1);
+ }
+ snippetTable.setRedraw(true);
+ }
+
+ /**
+ * Initialize the table item with information from the Snippet.
+ *
+ * @param item table item to initialize (not <code>null</code>)
+ * @param snippet source Snippet (not <code>null</code>)
+ */
+ private void fillTableItem(TableItem item, Snippet snippet) {
+ item.setData(snippet);
+
+ final String shortDescription;
+ if (snippet.description == null) {
+ shortDescription = "";
+ } else {
+ int index = snippet.description.indexOf('\n');
+ if (index < 0) {
+ index = snippet.description.length();
+ }
+ if (index > MAX_DESCRIPTION_LENGTH_IN_TABLE) {
+ shortDescription = snippet.description.substring(0, MAX_DESCRIPTION_LENGTH_IN_TABLE) + "...";
+ } else {
+ shortDescription = snippet.description.substring(0, index);
+ }
+ }
+
+ item.setText(new String[] { snippet.snippetName, shortDescription });
+ }
+
+ /**
+ * Process UI event queue until explorer is closed or otherwise ended.
+ */
+ private void runEventLoop() {
+ // Apart from the usual "dispatch events until closed" pattern the
+ // SnippetExplorer supports the special workflow where it close itself, run one
+ // or more Snippets one after another and then restarts the explorer itself
+ // which is all handled in this method.
+ try {
+ while (true) {
+ serialSnippets = null;
+ while (!shell.isDisposed()) {
+ if (!display.readAndDispatch()) {
+ display.sleep();
+ }
+ }
+
+ if (serialSnippets == null || serialSnippets.isEmpty()) {
+ break;
+ }
+
+ display.dispose();
+ int i = 0;
+ for (Snippet snippet : serialSnippets) {
+ System.out.println(String.format("(%d/%d) %s", ++i, serialSnippets.size(), snippet.snippetName));
+ runSnippetInCurrentThread(snippet);
+ }
+ final Display currentDisplay = Display.getCurrent();
+ if (currentDisplay != null) {
+ // left over from the snippet run
+ currentDisplay.dispose();
+ }
+ initialize();
+ final int index = runnerMapping.indexOf(null);
+ if (index != -1) {
+ runnerCombo.select(index);
+ }
+ }
+ } finally {
+ stopSnippets();
+ }
+ }
+
+ /** Try to stop all running Snippets. */
+ private synchronized void stopSnippets() {
+ for (SnippetRunner runner : runnerMapping) {
+ if (runner != null) {
+ runner.stopSnippets();
+ }
+ }
+ }
+
+ /**
+ * Launch the given snippet items with the currently selected snippet runner.
+ * <p>
+ * The items must contain the {@link Snippet} metadata as data object.
+ * </p>
+ *
+ * @param items the Snippets to launch
+ * @see #snippetRunner
+ */
+ private void launchSnippet(TableItem... items) {
+ final List<Snippet> validSnippets = new ArrayList<>();
+ for (TableItem item : items) {
+ if (item != null && item.getData() instanceof Snippet) {
+ validSnippets.add((Snippet) item.getData());
+ }
+ }
+
+ if (validSnippets.size() > START_MANY_SNIPPETS_WARNING_THREASHOLD) {
+ final MessageBox warnBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.YES | SWT.NO);
+ warnBox.setText("Starting many Snippets");
+ warnBox.setMessage("You have selected " + validSnippets.size() + " Snippets to start.\n"
+ + "Do you really want to start so many Snippets at once?");
+ if (warnBox.open() != SWT.YES) {
+ return;
+ }
+ }
+
+ if (snippetRunner != null) {
+ snippetRunner.launchSnippet(validSnippets.toArray(new Snippet[0]));
+ } else {
+ nextExplorerLocation = shell.getLocation();
+ serialSnippets = validSnippets;
+ shell.close();
+ }
+ }
+
+ /**
+ * Launches the given Snippet in the current thread by invoking the Snippets
+ * <code>main</code> method.
+ *
+ * @param snippet the Snippet to run (not <code>null</code>)
+ */
+ private static void runSnippetInCurrentThread(Snippet snippet) {
+ final Method method;
+ final String[] arguments = snippet.arguments;
+ try {
+ method = snippet.snippetClass.getMethod("main", arguments.getClass());
+ } catch (NoSuchMethodException ex) {
+ System.err.println("Did not find main(String []) for " + snippet.snippetName);
+ return;
+ }
+ try {
+ method.invoke(null, new Object[] { arguments });
+ } catch (IllegalAccessException | IllegalArgumentException e) {
+ System.err.println("Failed to launch " + snippet.snippetName + ". Error: " + e);
+ } catch (InvocationTargetException e) {
+ System.err.println("Exception in Snippet " + snippet.snippetName + ": " + e.getTargetException());
+ }
+ }
+
+ /**
+ * Show a warning dialog that the Snippet may print with the default printer
+ * without further warnings.
+ *
+ * @param shell parent shell for the warning dialog
+ * @param snippetName the Snippet's name to warn for
+ * @return <code>true</code> if the user confirmed Snippet execution
+ */
+ private static boolean printerWarning(Shell shell, String snippetName) {
+ final MessageBox warnBox = new MessageBox(shell, SWT.ICON_WARNING | SWT.OK | SWT.CANCEL);
+ warnBox.setText("Printing Snippet");
+ warnBox.setMessage(
+ snippetName + " may print something on your default printer without further warning or confirmation.");
+ return (warnBox.open() == SWT.OK);
+ }
+
+ /** Class to store metadata for a Snippet. */
+ private static class Snippet {
+ /** The Snippet's number. (not all Snippets may have numbers in the future) */
+ private int snippetNum;
+ /** Snippet's name / main class name. */
+ private String snippetName;
+ /** Snippet's main class. */
+ private Class<?> snippetClass;
+ /** Snippet's source code or <code>null</code> if not available. */
+ private String source;
+ /**
+ * Snippet description extracted from its source code. (may be
+ * <code>null</code>)
+ */
+ private String description;
+ /**
+ * Arguments used when launching the Snippets. Can be configured in
+ * {@link SnippetsConfig#getSnippetArguments(int)}.
+ */
+ private String[] arguments;
+
+ public Snippet(int snippetNum, String snippetName, Class<?> snippetClass, String source, String description,
+ String[] arguments) {
+ super();
+ this.snippetNum = snippetNum;
+ this.snippetName = snippetName;
+ this.snippetClass = snippetClass;
+ this.source = source;
+ this.description = description;
+ this.arguments = arguments;
+ }
+
+ @Override
+ public String toString() {
+ return "Snippet [snippetNum=" + snippetNum + ", snippetName=" + snippetName + ", snippetClass="
+ + snippetClass + ", source=" + source + ", description=" + description + ", arguments="
+ + Arrays.toString(arguments) + "]";
+ }
+ }
+
+ /** Interface for a runner capable to launch Snippets. */
+ private interface SnippetRunner {
+ /**
+ * Launch the given Snippets in the runner specific way.
+ *
+ * @param snippets Snippets to launch. Not <code>null</code>.
+ */
+ void launchSnippet(Snippet... snippets);
+
+ /**
+ * Stop all running Snippets launched with this runner. Some runners may not be
+ * able to stop Snippets.
+ */
+ void stopSnippets();
+ }
+
+ /** Run Snippets in separate threads. */
+ private class SnippetRunnerThread implements SnippetRunner {
+
+ /** All currently <b>running</b> Snippets launched from this runner. */
+ private final List<Thread> launchedSnippets = new ArrayList<>();
+
+ /**
+ * Launch Snippets parallel in separate threads. Call returns immediately after
+ * all Snippets are started.
+ */
+ @Override
+ public void launchSnippet(Snippet... snippets) {
+ for (Snippet snippet : snippets) {
+ if (snippet == null) {
+ return;
+ }
+ final Thread thread = new Thread(() -> {
+ try {
+ synchronized (launchedSnippets) {
+ launchedSnippets.add(Thread.currentThread());
+ }
+
+ // warn user before printing
+ if (SnippetsConfig.isPrintingSnippet(snippet.snippetNum)) {
+ final Display d = new Display();
+ try {
+ if (!printerWarning(new Shell(d), snippet.snippetName)) {
+ return;
+ }
+ } finally {
+ d.dispose();
+ }
+ }
+
+ runSnippetInCurrentThread(snippet);
+
+ } finally {
+ synchronized (launchedSnippets) {
+ final Display d = Display.getCurrent();
+ if (d != null) {
+ d.dispose();
+ }
+ launchedSnippets.remove(Thread.currentThread());
+ }
+ }
+ });
+ thread.setDaemon(true);
+ thread.setName(snippet.snippetName);
+ thread.start();
+ }
+ }
+
+ /**
+ * Stops all running Snippets launched by this runner. If a Snippt refuses to
+ * react to this stop signal it will not be force stopped until the
+ * SnippetExplorer itself is closed.
+ */
+ @Override
+ public void stopSnippets() {
+ final List<Thread> runningSnippets;
+ synchronized (launchedSnippets) {
+ runningSnippets = new ArrayList<>(launchedSnippets);
+ }
+ for (Thread t : runningSnippets) {
+ t.interrupt();
+ final Display d = Display.findDisplay(t);
+ if (d != null) {
+ d.asyncExec(
+ () -> Arrays.stream(d.getShells()).filter(s -> !s.isDisposed()).forEach(s -> s.close()));
+ }
+ }
+ final long start = System.currentTimeMillis();
+ for (Thread t : runningSnippets) {
+ if (System.currentTimeMillis() - SHUTDOWN_GRACE_TIME_MS > start) {
+ break;
+ }
+ try {
+ t.join(200);
+ } catch (InterruptedException e) {
+ }
+ }
+ synchronized (launchedSnippets) {
+ if (!launchedSnippets.isEmpty()) {
+ System.err.println("Some Snippets are still running:");
+ for (Thread t : launchedSnippets) {
+ System.err.println(" " + t.getName() + " (ThreadId: " + t.getId() + ")");
+ final Display d = Display.findDisplay(t);
+ if (d != null && !d.isDisposed()) {
+ d.syncExec(() -> d.dispose());
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /** Run Snippets in separate processes. */
+ private class SnippetRunnerProcess implements SnippetRunner {
+ /**
+ * All Snippets launched from this runner. Listed Snippets may already
+ * terminated.
+ */
+ private List<Process> launchedSnippets = new ArrayList<>();
+
+ /**
+ * Launch Snippets parallel as separate processes using the auto discovered JRE.
+ * Call returns immediately after all Snippets are started.
+ */
+ @Override
+ public synchronized void launchSnippet(Snippet... snippets) {
+ for (Snippet snippet : snippets) {
+ if (snippet == null) {
+ continue;
+ }
+
+ // warn user before printing
+ if (SnippetsConfig.isPrintingSnippet(snippet.snippetNum)) {
+ if (!printerWarning(shell, snippet.snippetName)) {
+ continue;
+ }
+ }
+
+ final List<String> command = new ArrayList<>();
+ command.add(javaCommand);
+ final String os = System.getProperty("os.name");
+ if (os != null && os.toLowerCase().contains("mac")) {
+ command.add("-XstartOnFirstThread");
+ }
+ final String cp = System.getProperty("java.class.path");
+ if (cp != null && !cp.isEmpty()) {
+ command.add("-cp");
+ command.add(cp);
+ }
+ final String libPath = System.getProperty("java.library.path");
+ if (libPath != null && !libPath.isEmpty()) {
+ command.add("-Djava.library.path=" + libPath);
+ }
+ command.add(SnippetsConfig.SNIPPETS_PACKAGE + "." + snippet.snippetName);
+ command.addAll(Arrays.asList(snippet.arguments));
+ try {
+ System.out.println("Exec: " + String.join(" ", command));
+ ProcessBuilder processBuilder = new ProcessBuilder(command);
+ processBuilder.redirectOutput(Redirect.INHERIT);
+ processBuilder.redirectError(Redirect.INHERIT);
+ final Process p = processBuilder.start();
+ launchedSnippets.add(p);
+ } catch (IOException e) {
+ System.err.println("Failed to launch " + snippet.snippetName + ". Error: " + e);
+ }
+ }
+ }
+
+ /**
+ * Stops all running Snippets launched by this runner. If the stop signal was
+ * send but the Snippet is still running after a short grace time the runner
+ * tries to stop the Snippet forcefully.
+ * <p>
+ * If all attempts to stop the Snippet fail then the Snippet will run even after
+ * the SnippetExplorer was closed.
+ * </p>
+ */
+ @Override
+ public synchronized void stopSnippets() {
+ for (Process p : launchedSnippets) {
+ p.destroy();
+ }
+
+ final long start = System.currentTimeMillis();
+ while (!launchedSnippets.isEmpty() && System.currentTimeMillis() - SHUTDOWN_GRACE_TIME_MS < start) {
+ final Iterator<Process> it = launchedSnippets.iterator();
+ while (it.hasNext()) {
+ final Process p = it.next();
+ if (!p.isAlive()) {
+ it.remove();
+ }
+ }
+ if (!launchedSnippets.isEmpty()) {
+ try {
+ launchedSnippets.get(0).waitFor(100, TimeUnit.MILLISECONDS);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+
+ if (!launchedSnippets.isEmpty()) {
+ System.err.println(launchedSnippets.size() + " Snippets are still running.");
+ for (Process p : launchedSnippets) {
+ p.destroyForcibly();
+ }
+ final Iterator<Process> it = launchedSnippets.iterator();
+ while (it.hasNext()) {
+ final Process p = it.next();
+ if (!p.isAlive()) {
+ it.remove();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Update thread used to delay the list filtering due to changed filter string.
+ */
+ private class ListUpdater extends Thread {
+ /**
+ * The timestamp in milliseconds since epoch when the next update should be
+ * executed.
+ */
+ private long nextListUpdate = 0;
+
+ public ListUpdater() {
+ setName("List Updater");
+ setDaemon(true);
+ }
+
+ /**
+ * Reapply the table filter in X milliseconds.
+ * <p>
+ * If an update is already scheduled only the latest update time will be used.
+ * </p>
+ *
+ * @param ms sleep time before updating the main table
+ */
+ public synchronized void updateInMs(long ms) {
+ if (ms < 0) {
+ return;
+ }
+ final long nextUpdate = System.currentTimeMillis() + ms;
+ if (nextListUpdate < nextUpdate) {
+ nextListUpdate = nextUpdate;
+ }
+ notify();
+ }
+
+ @Override
+ public void run() {
+ while (!isInterrupted()) {
+ final long nextUpdate;
+ synchronized (this) {
+ nextUpdate = nextListUpdate;
+ }
+ if (nextUpdate - System.currentTimeMillis() <= 0) {
+ if (filterField != null) {
+ display.syncExec(() -> updateTable(filterField.getText()));
+ }
+ synchronized (this) {
+ if (nextUpdate == nextListUpdate) {
+ // no new update was scheduled while updating
+ nextListUpdate = 0;
+ }
+ }
+ }
+ synchronized (this) {
+ long sleepTime = nextListUpdate;
+ if (sleepTime != 0) {
+ sleepTime -= System.currentTimeMillis();
+ if (sleepTime <= 0) {
+ sleepTime = 1;
+ }
+ }
+ try {
+ wait(sleepTime);
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetLauncher.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetLauncher.java
index 6d56afe960..5c49658464 100644
--- a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetLauncher.java
+++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetLauncher.java
@@ -13,7 +13,6 @@
*******************************************************************************/
package org.eclipse.swt.snippets;
-import java.io.*;
/*
* Simple "hackable" code that runs all of the SWT Snippets,
* typically for testing. One example of a useful "hack" is
@@ -23,6 +22,7 @@ import java.io.*;
* String source = String.valueOf(buffer);
* in order to run all of the Table and Tree Snippets.
*/
+import java.io.*;
import java.lang.reflect.*;
import org.eclipse.swt.*;
@@ -30,7 +30,7 @@ import org.eclipse.swt.*;
public class SnippetLauncher {
public static void main (String [] args) {
- File sourceDir = new File("src/org/eclipse/swt/snippets");
+ File sourceDir = SnippetsConfig.SNIPPETS_SOURCE_DIR;
boolean hasSource = sourceDir.exists();
int count = 500;
if (hasSource) {
@@ -38,11 +38,11 @@ public class SnippetLauncher {
if (files.length > 0) count = files.length;
}
for (int i = 1; i < count; i++) {
- if (i == 132 || i == 133 || i == 318) continue; // avoid printing to printer
+ if (SnippetsConfig.isPrintingSnippet(i)) continue; // avoid printing to printer
String className = "Snippet" + i;
Class<?> clazz = null;
try {
- clazz = Class.forName("org.eclipse.swt.snippets." + className);
+ clazz = Class.forName(SnippetsConfig.SNIPPETS_PACKAGE + "." + className);
} catch (ClassNotFoundException e) {}
if (clazz != null) {
System.out.println("\n" + clazz.getName());
@@ -85,8 +85,7 @@ public class SnippetLauncher {
}
}
Method method = null;
- String [] param = new String [0];
- if (i == 81) param = new String[] {"Shell.Explorer"};
+ String [] param = SnippetsConfig.getSnippetArguments(i);
try {
method = clazz.getMethod("main", param.getClass());
} catch (NoSuchMethodException e) {
diff --git a/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetsConfig.java b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetsConfig.java
new file mode 100644
index 0000000000..16a25cb43a
--- /dev/null
+++ b/examples/org.eclipse.swt.snippets/src/org/eclipse/swt/snippets/SnippetsConfig.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Paul Pazderski and others.
+ *
+ * This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Paul Pazderski - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.swt.snippets;
+
+import java.io.*;
+
+/**
+ * Used to store some metadata, argument or configuration stuff for snippets.
+ * Most of this was hardcoded in {@link SnippetLauncher} in the past.
+ */
+public class SnippetsConfig {
+
+ public static final File SNIPPETS_SOURCE_DIR = new File("src/org/eclipse/swt/snippets");
+
+ public static final String SNIPPETS_PACKAGE = "org.eclipse.swt.snippets";
+
+ public static boolean isPrintingSnippet(int snippetNumber) {
+ return snippetNumber == 132 || snippetNumber == 133 || snippetNumber == 318;
+ }
+
+ public static String[] getSnippetArguments(int snippetNumber) {
+ switch (snippetNumber) {
+ case 81:
+ return new String[] { "Shell.Explorer" };
+
+ default:
+ return new String[0];
+ }
+ }
+}

Back to the top