Gridarta Editor
GridartaEditor.java
Go to the documentation of this file.
1 /*
2  * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games.
3  * Copyright (C) 2000-2015 The Gridarta Developers.
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18  */
19 
20 package net.sf.gridarta.maincontrol;
21 
22 import gnu.getopt.Getopt;
23 import gnu.getopt.LongOpt;
24 import java.awt.GraphicsEnvironment;
25 import java.io.File;
26 import java.lang.reflect.InvocationTargetException;
27 import java.util.Arrays;
28 import java.util.List;
29 import java.util.Locale;
30 import java.util.MissingResourceException;
31 import java.util.ResourceBundle;
32 import java.util.prefs.Preferences;
33 import javax.swing.SwingUtilities;
34 import javax.swing.UIManager;
35 import javax.swing.UnsupportedLookAndFeelException;
36 import net.sf.gridarta.MainControl;
66 import net.sf.japi.swing.action.ActionBuilder;
67 import net.sf.japi.swing.action.ActionBuilderFactory;
68 import org.apache.log4j.Category;
69 import org.apache.log4j.Logger;
70 import org.jetbrains.annotations.NotNull;
71 import org.jetbrains.annotations.Nullable;
72 
80 @SuppressWarnings("UseOfSystemOutOrSystemErr")
81 public class GridartaEditor<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> {
82 
86  @NotNull
87  private static final Category LOG = Logger.getLogger(GridartaEditor.class);
88 
93  public GridartaEditor(@NotNull final String tipOfTheDayPackage) {
94  System.setProperty("net.sf.japi.swing.tod", tipOfTheDayPackage);
95  try {
96  if (LOG.isInfoEnabled()) {
97  LOG.info("build number: " + ResourceBundle.getBundle("build").getString("build.number"));
98  }
99  } catch (final MissingResourceException e) {
100  LOG.warn("No build number found:", e);
101  }
102  }
103 
114  public void run(@NotNull final String actionBuilderPackage, @NotNull final String editorJarName, @NotNull final EditorFactory<G, A, R> editorFactory, @Nullable final String defaultConfig, @NotNull final String... args) {
115  boolean err = false; // whether a fatal error (exit required) occurred.
116  String plugin = null;
117  final LongOpt[] longOpts = { new LongOpt("batchpng", LongOpt.NO_ARGUMENT, null, (int) 'b'), new LongOpt("normal", LongOpt.NO_ARGUMENT, null, (int) 'n'), new LongOpt("singlepng", LongOpt.NO_ARGUMENT, null, (int) 's'), new LongOpt("collectarches", LongOpt.NO_ARGUMENT, null, (int) 'c'), new LongOpt("collectArches", LongOpt.NO_ARGUMENT, null, (int) 'c'), new LongOpt("help", LongOpt.NO_ARGUMENT, null, (int) 'h'), new LongOpt("noexit", LongOpt.NO_ARGUMENT, null, 2), new LongOpt("script", LongOpt.REQUIRED_ARGUMENT, null, 1), new LongOpt("plugin", LongOpt.REQUIRED_ARGUMENT, null, 1), new LongOpt("config", LongOpt.REQUIRED_ARGUMENT, null, 3), };
118  final Getopt g = new Getopt(editorJarName, args, "bchns", longOpts);
120  File config = defaultConfig == null ? null : ConfigFileUtils.getHomeFile(defaultConfig);
121  boolean doExit = true;
122  while (true) {
123  final int ch = g.getopt();
124  if (ch == -1) {
125  break;
126  }
127 
128  switch (ch) {
129  case 'b':
130  mode = GridartaRunMode.BATCH_PNG;
131  break;
132 
133  case 'c':
135  break;
136 
137  case 'h':
138  usage(editorJarName, defaultConfig);
139  return;
140 
141  case 'n':
142  mode = GridartaRunMode.NORMAL;
143  break;
144 
145  case 's':
147  break;
148 
149  case 1:
150  plugin = g.getOptarg();
151  break;
152 
153  case 2:
154  doExit = false;
155  break;
156 
157  case 3: // --config
158  config = new File(g.getOptarg());
159  if (!config.exists()) {
160  System.err.println(config + ": configuration file does not exist");
161  err = true;
162  }
163  break;
164 
165  case '?':
166  default:
167  err = true;
168  break;
169  }
170  }
171  int returnCode;
172  if (err) {
173  returnCode = 1;
174  } else {
175  if (config != null) {
176  System.setProperty("java.util.prefs.PreferencesFactory", "net.sf.gridarta.preferences.FilePreferencesFactory");
177  FilePreferencesFactory.initialize("user_net_sf_gridarta", config);
178  }
179 
180  // Make sure the locale is set before any ActionBuilder is used.
181  final Preferences preferences = Preferences.userNodeForPackage(MainControl.class);
182  final String locName = preferences.get(GUIPreferences.PREFERENCES_LANGUAGE, null);
183  if (locName != null) {
184  Locale.setDefault(new Locale(locName));
185  }
186 
187  final List<String> args2 = Arrays.asList(args).subList(g.getOptind(), args.length);
188 
189  // print jre version, for easier recognition of jre-specific problems
190  System.err.println("Running java version " + System.getProperty("java.version"));
191  // Now add preferences to the ActionBuilder.
192  final ActionBuilder actionBuilder = ActionBuilderFactory.getInstance().getActionBuilder("net.sf.gridarta");
193  actionBuilder.addPref(DefaultMainControl.class);
194  actionBuilder.addParent(ActionBuilderFactory.getInstance().getActionBuilder(actionBuilderPackage));
195 
196  // Create the application and give it the parameters
197  final ErrorView errorView = GraphicsEnvironment.isHeadless() || mode.isConsoleMode() || plugin != null ? new ConsoleErrorView() : new DefaultErrorView(null);
198  final ResourceIcons resourceIcons = new ResourceIcons();
199  final ConfigSourceFactory configSourceFactory = new DefaultConfigSourceFactory();
200  final EditorSettings editorSettings = new DefaultEditorSettings();
201  final ProjectSettings projectSettings = editorFactory.newProjectSettings(editorSettings);
202  final ConfigSource configSource = configSourceFactory.getConfigSource(mode == GridartaRunMode.COLLECT_ARCHES ? "ARCH_DIRECTORY" : projectSettings.getConfigSourceName());
203  final ProjectModel<G, A, R> projectModel = new ProjectModel<>(errorView, projectSettings, editorFactory, resourceIcons, configSource);
204 
205  if (plugin != null) {
206  returnCode = runPlugin(plugin, errorView, args2, editorFactory, projectModel, resourceIcons, editorSettings);
207  } else {
208  try {
209  switch (mode) {
210  case NORMAL:
211  returnCode = runNormal(args2, editorFactory, errorView, resourceIcons, configSourceFactory, projectModel, editorSettings);
212  break;
213 
214  case BATCH_PNG:
215  returnCode = new BatchPngCommand(args2, ImageCreatorFactory.newImageCreator(resourceIcons, editorFactory, projectModel, editorSettings)).execute();
216  break;
217 
218  case SINGLE_PNG:
219  if (args2.size() != 2) {
220  throw new SyntaxErrorException("input output");
221  }
222 
223  returnCode = new SinglePngCommand(new File(args2.get(0)), new File(args2.get(1)), ImageCreatorFactory.newImageCreator(resourceIcons, editorFactory, projectModel, editorSettings)).execute();
224  break;
225 
226  case COLLECT_ARCHES:
227  returnCode = new CollectArchesCommand(projectModel).execute();
228  break;
229 
230  default:
231  assert false;
232  returnCode = 1;
233  break;
234  }
235  } catch (final SyntaxErrorException ex) {
236  System.err.println("Usage: " + ex.getMessage());
237  returnCode = 1;
238  }
239  }
240  }
241  if (doExit && (mode != GridartaRunMode.NORMAL || returnCode != 0)) {
242  System.exit(returnCode);
243  }
244  }
245 
257  private int runPlugin(@NotNull final String plugin, final ErrorView errorView, final Iterable<String> args, @NotNull final EditorFactory<G, A, R> editorFactory, @NotNull final ProjectModel<G, A, R> projectModel, @NotNull final ResourceIcons resourceIcons, @NotNull final EditorSettings editorSettings) {
258  checkForErrors(errorView);
259  waitDialog(errorView);
260  final NamedFilter defaultNamedFilterList = new NamedFilter(projectModel.getGameObjectMatchers().getFilters());
261  final FilterControl<G, A, R> filterControl = new DefaultFilterControl<>(defaultNamedFilterList, editorSettings);
262  final MapViewSettings mapViewSettings = new DefaultMapViewSettings();
263  final RendererFactory<G, A, R> rendererFactory = editorFactory.newRendererFactory(mapViewSettings, filterControl, projectModel.getGameObjectParser(), projectModel.getFaceObjectProviders(), resourceIcons, projectModel.getSmoothFaces());
264  return new PluginExecutor<>(projectModel.getPluginModel(), projectModel.newPluginParameters(rendererFactory)).executePlugin(plugin, args);
265  }
266 
272  private static void checkForErrors(@NotNull final ErrorView errorView) {
273  if (errorView.hasErrors()) {
274  waitDialog(errorView);
276  throw new AssertionError();
277  }
278  }
279 
284  private static void waitDialog(final ErrorView errorView) {
285  try {
286  errorView.waitDialog();
287  } catch (final InterruptedException ex) {
288  Thread.currentThread().interrupt();
290  throw new AssertionError(ex);
291  }
292  }
293 
305  private int runNormal(@NotNull final Iterable<String> args, @NotNull final EditorFactory<G, A, R> editorFactory, @NotNull final ErrorView errorView, @NotNull final ResourceIcons resourceIcons, @NotNull final ConfigSourceFactory configSourceFactory, @NotNull final ProjectModel<G, A, R> projectModel, @NotNull final EditorSettings editorSettings) {
306  try {
307  for (final UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
308  if ("Nimbus".equals(info.getName())) {
309  UIManager.setLookAndFeel(info.getClassName());
310  break;
311  }
312  }
313  } catch (final ClassNotFoundException ignored) {
314  } catch (final InstantiationException ignored) {
315  } catch (final IllegalAccessException ignored) {
316  } catch (final UnsupportedLookAndFeelException ignored) {
317  }
318 
319  final GUIMainControl<?, ?, ?>[] guiMainControl = new GUIMainControl<?, ?, ?>[1];
320  final Runnable runnable = new Runnable() {
321 
322  @Override
323  public void run() {
324  guiMainControl[0] = new GUIMainControl<>(projectModel, editorSettings, errorView, resourceIcons, editorFactory, configSourceFactory);
325  }
326 
327  };
328  try {
329  SwingUtilities.invokeAndWait(runnable);
330  } catch (final InterruptedException ex) {
331  LOG.fatal(ex.getMessage(), ex);
332  Thread.currentThread().interrupt();
333  return 1;
334  } catch (final InvocationTargetException ex) {
335  LOG.fatal(ex.getMessage(), ex);
336  return 1;
337  }
338 
339  checkForErrors(errorView);
340  waitDialog(errorView);
341 
342  final Runnable runnable2 = new Runnable() {
343 
344  @Override
345  public void run() {
346  guiMainControl[0].run(args);
347  }
348 
349  };
350  try {
351  SwingUtilities.invokeAndWait(runnable2);
352  } catch (final InterruptedException ex) {
353  LOG.fatal(ex.getMessage(), ex);
354  Thread.currentThread().interrupt();
355  return 1;
356  } catch (final InvocationTargetException ex) {
357  LOG.fatal(ex.getMessage(), ex);
358  return 1;
359  }
360 
361  return 0;
362  }
363 
370  private static void usage(@NotNull final String editorJarName, @Nullable final String defaultConfig) {
371  System.out.println("usage: java -jar " + editorJarName + " [option...] [map-file...]");
372  System.out.println("");
373  System.out.println(" -h, --help print this help");
374  System.out.println(" -b, --batchpng create PNG files for all given maps in their directories");
375  System.out.println(" -c, --collectarches collect archetypes");
376  System.out.println(" -n, --normal start editor with GUI (default)");
377  System.out.println(" -s, --singlepng create a PNG file from the specified map");
378  System.out.println(" --config=config-file use given config file; uses " + (defaultConfig == null ? "Java preferences" : "~/.gridarta/" + defaultConfig) + " is not given");
379  System.out.println(" --noexit do not call System.exit()");
380  System.out.println(" --plugin=name [arg=value...]");
381  System.out.println(" run a plugin with the given arguments");
382  }
383 
384 }
A Filter that aggregates named filters.
static final String PREFERENCES_LANGUAGE
Preferences key for language.
boolean isConsoleMode()
Returns whether this mode uses console-mode.
Graphical User Interface of Gridarta.
int runPlugin(@NotNull final String plugin, final ErrorView errorView, final Iterable< String > args, @NotNull final EditorFactory< G, A, R > editorFactory, @NotNull final ProjectModel< G, A, R > projectModel, @NotNull final ResourceIcons resourceIcons, @NotNull final EditorSettings editorSettings)
Executes a plugin.
Settings that apply to a project.
static File getHomeFile(@NotNull final String filename)
Return the filename to use when dealing with this application&#39;s and current users&#39; home directory...
Default implementation of EditorSettings.
Allows execution of Plugins.
void run(@NotNull final String actionBuilderPackage, @NotNull final String editorJarName, @NotNull final EditorFactory< G, A, R > editorFactory, @Nullable final String defaultConfig, @NotNull final String... args)
Runs the editor.
GridartaEditor(@NotNull final String tipOfTheDayPackage)
Creates a new instance.
SINGLE_PNG
Single PNG: Create a PNG file from the specified map.
Interface for classes displaying error messages.
Definition: ErrorView.java:28
Base package of all Gridarta classes.
Reflects a game object (object on a map).
Definition: GameObject.java:36
static void checkForErrors(@NotNull final ErrorView errorView)
Checks whether a ErrorView instance contains at least one error.
Loader for loading resources the user&#39;s home directory.
This package contains the preferences ui modules.
Possible source locations for configuration files.
static void usage(@NotNull final String editorJarName, @Nullable final String defaultConfig)
Prints the editor&#39;s command-line options to System#out.
Container for settings that affect the rendering of maps.
GameObjects are the objects based on Archetypes found on maps.
Interface used as preferences location.
int runNormal(@NotNull final Iterable< String > args, @NotNull final EditorFactory< G, A, R > editorFactory, @NotNull final ErrorView errorView, @NotNull final ResourceIcons resourceIcons, @NotNull final ConfigSourceFactory configSourceFactory, @NotNull final ProjectModel< G, A, R > projectModel, @NotNull final EditorSettings editorSettings)
Run in normal mode.
The filter package contains the classes for Filters.
Definition: BtnPopup.java:20
An ErrorView that reports all errors to the console.
String getConfigSourceName()
Returns the name of the configuration source.
Exception thrown for incorrect arguments.
MainControl is a central class that&#39;s used for access on global data structures / collections and glo...
static void callExit(final int returnCode)
Calls System#exit(int) or does nothing depending on the user&#39;s settings.
Base classes for rendering maps.
Creates the main GUI of Gridarta.
Utility class for creating ImageCreator instances.
Factory for creating MapRenderer instances.
A ConfigSourceFactory that is configured through action keys.
BATCH_PNG
Batch PNG: Create PNG files for all given maps in their directories.
Creates ImageIcon instances from resources.
ConfigSource getConfigSource(@NotNull String name)
Returns a ConfigSource by name.
Main class of the editor; parses command-line arguments, initializes and starts the editor...
void waitDialog()
Waits until the dialog has been dismissed.
static void initialize(@NotNull final String defaultName, @Nullable final File file)
Initialize the module.
void run(@NotNull final Iterable< String > args)
Starts the editor: makes the main window visible and opens map files.
NORMAL
Normal operation: Start editor with GUI.
Settings that apply to the editor.
A PreferencesFactory which creates FilePreferences instances.
A dialog displaying a tree of error messages.
static void waitDialog(final ErrorView errorView)
Waits until the error dialog has been dismissed.
Preferences Module for user interface preferences.