Gridarta Editor
SpellsUtils.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.gui.spells;
21 
22 import java.awt.Component;
23 import java.io.BufferedReader;
24 import java.io.EOFException;
25 import java.io.File;
26 import java.io.FileInputStream;
27 import java.io.FileNotFoundException;
28 import java.io.FileOutputStream;
29 import java.io.IOException;
30 import java.io.InputStream;
31 import java.io.InputStreamReader;
32 import java.io.OutputStreamWriter;
33 import java.io.PrintWriter;
34 import java.io.Reader;
35 import java.nio.charset.StandardCharsets;
36 import java.util.Date;
37 import java.util.Map;
38 import java.util.TreeMap;
39 import javax.swing.JFileChooser;
40 import javax.swing.filechooser.FileFilter;
42 import net.sf.japi.swing.action.ActionBuilder;
43 import net.sf.japi.swing.action.ActionBuilderFactory;
44 import net.sf.japi.util.filter.file.FilenameFileFilter;
45 import org.apache.log4j.Category;
46 import org.apache.log4j.Logger;
47 import org.jetbrains.annotations.NotNull;
48 import org.jetbrains.annotations.Nullable;
49 
53 public class SpellsUtils {
54 
58  private static final long READ_MAX = 10000L;
59 
63  @NotNull
64  private static final Category LOG = Logger.getLogger(SpellsUtils.class);
65 
69  @NotNull
70  private static final ActionBuilder ACTION_BUILDER = ActionBuilderFactory.getInstance().getActionBuilder("net.sf.gridarta");
71 
75  @NotNull
76  private static final FileFilter SPELL_LIST_H_FILE_FILTER = new FilenameFileFilter(true, "spellist.h", "spellist.h");
77 
81  @NotNull
82  private final String spellFile;
83 
88  public SpellsUtils(@NotNull final String spellFile) {
89  this.spellFile = spellFile;
90  }
91 
97  public void importSpells(@NotNull final File dir, @NotNull final Component parent) {
98  // open a file chooser window
99  final JFileChooser fileChooser = new JFileChooser();
100  fileChooser.setDialogTitle("Open CF Spell List File");
101  fileChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
102  fileChooser.setMultiSelectionEnabled(false);
103  fileChooser.setFileFilter(SPELL_LIST_H_FILE_FILTER); // apply file filter
104  final File cd = new File(System.getProperty("user.dir"));
105  final File sd = new File(cd, "../server/src/include");
106  FileChooserUtils.setCurrentDirectory(fileChooser, sd.exists() ? sd : cd);
107 
108  final int returnVal = fileChooser.showOpenDialog(parent);
109 
110  if (returnVal == JFileChooser.APPROVE_OPTION) {
111  // now import spells from selected file
112  final File tmpSpellFile = fileChooser.getSelectedFile();
113  final int spNum = importSpells(tmpSpellFile, dir);
114  if (spNum > 0) {
115  // yeah it worked
116  ACTION_BUILDER.showMessageDialog(parent, "importSpellsSuccess", spNum);
117  } else {
118  // spell collect failed
119  ACTION_BUILDER.showMessageDialog(parent, "importSpellsFailed");
120  }
121  }
122  }
123 
131  private int importSpells(final File spellFile, @NotNull final File dir) {
132  final Map<String, String> spells = new TreeMap<>();
133  if (spellFile.getName().equalsIgnoreCase("spellist.h")) {
134  try {
135  try (InputStream fis = new FileInputStream(spellFile.getAbsolutePath())) {
136  try (Reader isr = new InputStreamReader(fis)) {
137  try (Reader in = new BufferedReader(isr)) {
138  readUntil(in, "spell spells", null);
139  readUntil(in, "{", null);
140 
141  int counter = 0;
142  while (true) {
143  try {
144  readUntil(in, "{", "}");
145  } catch (final EOFException ignored) {
146  // Eventually expected exception, don't handle.
147  break;
148  }
149  readUntil(in, "\"", null);
150  final String name = readUntil(in, "\"").trim();
151  readUntil(in, "}", null);
152 
153  spells.put(name, Integer.toString(counter++));
154  }
155  }
156  }
157  }
158  } catch (final FileNotFoundException ignored) {
159  LOG.error("File '" + spellFile.getAbsolutePath() + "' not found!");
160  } catch (final IOException ex) {
161  LOG.error("Cannot read file '" + spellFile.getAbsolutePath() + "': " + ex.getMessage());
162  }
163  }
164 
165  // --------- now write the "spells.def" file ---------
166  if (!spells.isEmpty()) {
167  // FIXME: This should use DOM for writing the file.
168  // create new file for writing (replaces old one if existent)
169  if (!dir.exists() || !dir.isDirectory()) { // FIXME What if dir exists and is not a directory? mkdirs will fail then!
170  // create the config dir
171  dir.mkdirs();
172  }
173  final File dirFile = new File(dir, this.spellFile);
174 
175  try {
176  try (PrintWriter out = new PrintWriter(new OutputStreamWriter(new FileOutputStream(dirFile), StandardCharsets.UTF_8))) {
177  // header:
178  out.println("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
179  out.println("<!DOCTYPE spells SYSTEM \"spells.dtd\">");
180  out.println("<!--");
181  out.println(" - ##########################################################");
182  out.println(" - # You may add new spells to this file, but there's no #");
183  out.println(" - # need to do it because the file can be auto-generated. #");
184  out.println(" - # In the editor, select menu \"Resources->Collect Spells\" #");
185  out.println(" - # to generate a new version of this file. #");
186  out.println(" - ##########################################################");
187  out.println(" -->");
188  out.println("<!-- Generated on: " + new Date() + " -->");
189  out.println("<spells>");
190 
191  final String[] spaces = { " ", " ", "" };
192  for (final Map.Entry<String, String> entry : spells.entrySet()) {
193  final String id = entry.getValue();
194  out.println(" <spell id=\"" + id + '\"' + spaces[id.length() - 1] + " name=\"" + entry.getKey() + "\" />");
195  }
196  out.println("</spells>");
197  }
198  } catch (final IOException ex) {
199  LOG.error("Cannot write file '" + dirFile.getAbsolutePath() + "': " + ex.getMessage());
200  }
201  }
202  return spells.size();
203  }
204 
220  private static void readUntil(@NotNull final Reader stream, @NotNull final CharSequence tag, @Nullable final CharSequence abort) throws IOException {
221  int c; // character value, read from the stream
222  int t = 0; // tag index
223  int a = 0; // abort index
224 
225  if (abort != null) {
226  // look both for 'tag' and 'abort'
227  do {
228  c = stream.read();
229  if (c == tag.charAt(t)) {
230  t++;
231  } else {
232  t = 0;
233  }
234  if (c == abort.charAt(a)) {
235  a++;
236  } else {
237  a = 0;
238  }
239  } while (t < tag.length() && a < abort.length() && c != -1);
240  } else {
241  // look only for 'tag'
242  do {
243  c = stream.read();
244  if (c == tag.charAt(t)) {
245  t++;
246  } else {
247  t = 0;
248  }
249  } while (t < tag.length() && c != -1);
250  }
251 
252  // if we did not find the tag, an EOFException is thrown
253  if (c == -1) {
254  throw new EOFException();
255  }
256 
257  // if we found the string 'abort', throw EOFException as well
258  if (abort != null && a == abort.length()) {
259  throw new EOFException();
260  }
261  }
262 
274  private static String readUntil(@NotNull final Reader stream, @NotNull final CharSequence tag) throws IOException {
275  final StringBuilder sb = new StringBuilder();
276  int c; // character value, read from the stream
277  int t = 0; // index
278 
279  long count = 0L; // counter (to realize when shooting past EOF)
280  final long maxCount = READ_MAX; // bail out when counter exceeds this value
281 
282  do {
283  c = stream.read(); // read one character
284  sb.append((char) c);
285  if (c == tag.charAt(t)) {
286  t++;
287  } else {
288  t = 0;
289  }
290  } while (t < tag.length() && c != -1 && count++ < maxCount);
291 
292  // if we did not find the tag, an EOFException is thrown
293  if (c == -1 || count >= maxCount) {
294  throw new EOFException();
295  }
296  // cut 'tag' off, at the end of the string
297  return sb.substring(0, sb.length() - tag.length());
298  }
299 
300 }
void importSpells(@NotNull final File dir, @NotNull final Component parent)
Opens a file chooser to select the spell list file, then import spells.
Utility class for JFileChooser related functions.
static String readUntil(@NotNull final Reader stream, @NotNull final CharSequence tag)
Reads characters from the BufferedReader stream till &#39;tag&#39; is found.
static final Category LOG
The Logger for printing log messages.
Base package of all Gridarta classes.
static void setCurrentDirectory(@NotNull final JFileChooser fileChooser, @Nullable final File dir)
Calls JFileChooser#setCurrentDirectory(File).
static void readUntil(@NotNull final Reader stream, @NotNull final CharSequence tag, @Nullable final CharSequence abort)
Reads characters from the BufferedReader stream till &#39;tag&#39; is found.
final String spellFile
The spell file name.
int importSpells(final File spellFile, @NotNull final File dir)
Reads all spells from a Crossfire or Daimonin spell list file and write an alphabetical list into "sp...
static final long READ_MAX
Maximum number of characters to read in readUntil.
SpellsUtils(@NotNull final String spellFile)
Creates a new instance.
static final ActionBuilder ACTION_BUILDER
Action Builder.
static final FileFilter SPELL_LIST_H_FILE_FILTER
File filter for filtering spellist.h files.