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