Gridarta Editor
TreasureLoader.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.model.treasurelist;
21 
22 import java.io.BufferedReader;
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FilenameFilter;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.io.InputStreamReader;
29 import java.io.Reader;
30 import java.util.ArrayList;
31 import java.util.Arrays;
32 import java.util.Collection;
33 import java.util.Comparator;
34 import java.util.List;
35 import java.util.Map;
36 import javax.swing.tree.DefaultMutableTreeNode;
37 import javax.swing.tree.MutableTreeNode;
43 import net.sf.gridarta.utils.IOUtils;
44 import org.apache.log4j.Category;
45 import org.apache.log4j.Logger;
46 import org.jetbrains.annotations.NotNull;
47 import org.jetbrains.annotations.Nullable;
48 
54 public class TreasureLoader {
55 
59  @NotNull
60  private static final Category LOG = Logger.getLogger(TreasureLoader.class);
61 
66  @NotNull
67  private static final Comparator<TreasureTreeNode> TREASURE_NODE_COMPARATOR = new Comparator<TreasureTreeNode>() {
68 
69  @Override
70  public int compare(@NotNull final TreasureTreeNode o1, @NotNull final TreasureTreeNode o2) {
71  return String.CASE_INSENSITIVE_ORDER.compare(o1.getTreasureObj().getName(), o2.getTreasureObj().getName());
72  }
73 
74  };
75 
79  @NotNull
80  private static final FilenameFilter TREASURE_LIST_FILTER = new FilenameFilter() {
81 
82  @Override
83  public boolean accept(@NotNull final File dir, @NotNull final String name) {
84  if (name.startsWith(".")) {
85  return false;
86  }
87 
88  final File fullPath = new File(dir, name);
89  if (fullPath.isDirectory()) {
90  return true;
91  }
92 
93  final String lowerCaseName = name.toLowerCase();
94  return lowerCaseName.endsWith(".tl") || lowerCaseName.endsWith(".trs");
95  }
96 
97  };
98 
102  private TreasureLoader() {
103  }
104 
113  @NotNull
114  public static TreasureTree parseTreasures(@NotNull final ErrorView errorView, @NotNull final Map<String, TreasureTreeNode> specialTreasureLists, @NotNull final ConfigSource configSource, @NotNull final ProjectSettings projectSettings) {
115  final List<TreasureTreeNode> tmpList = new ArrayList<>(); // tmp. container for all treasurelists
116  final List<TreasureTreeNode> needLink = new ArrayList<>(); // all sub-treasurelist nodes that need linking
117 
118  // first step: parsing data file, adding all treasurelists to the tmpList vector
119  int index = 0;
120  while (true) {
121  final File treasureLocation;
122  try {
123  treasureLocation = configSource.getFile(projectSettings, "treasures", index);
124  } catch (final IOException ex) {
125  errorView.addWarning(ErrorViewCategory.TREASURES_FILE_INVALID, ex.getMessage());
126  index++;
127  continue;
128  }
129  if (treasureLocation == null) {
130  break;
131  }
132 
133  final int tmpListSize = tmpList.size();
134  if (treasureLocation.isDirectory()) {
135  loadTreasureDir(errorView, treasureLocation, tmpList, needLink);
136  } else if (treasureLocation.isFile()) {
137  loadTreasureList(errorView, treasureLocation, tmpList, needLink);
138  } else {
139  new ErrorViewCollector(errorView, treasureLocation).addWarning(ErrorViewCategory.TREASURES_FILE_INVALID, "location is neither file nor directory");
140  }
141  if (LOG.isInfoEnabled()) {
142  LOG.info("Loaded " + (tmpList.size() - tmpListSize) + " treasurelists from '" + treasureLocation + "'.");
143  }
144  index++;
145  }
146 
147  return createTreasureTree(tmpList, needLink, specialTreasureLists);
148  }
149 
158  @NotNull
159  private static TreasureTree createTreasureTree(@NotNull final List<TreasureTreeNode> tmpList, @NotNull final Iterable<TreasureTreeNode> needLink, @NotNull final Map<String, TreasureTreeNode> specialTreasureLists) {
160  final DefaultMutableTreeNode root = new DefaultMutableTreeNode("Treasurelists:");
161  final TreasureTree treasures = new TreasureTree(root);
162  treasures.putAll(tmpList);
163  addTopLevelEntries(tmpList, specialTreasureLists, root);
164  linkSubLists(needLink, treasures);
165  addSpecialEntries(specialTreasureLists, root);
166  return treasures;
167  }
168 
177  private static void addTopLevelEntries(@NotNull final List<TreasureTreeNode> tmpList, @NotNull final Map<String, TreasureTreeNode> specialTreasureLists, @NotNull final DefaultMutableTreeNode root) {
178  tmpList.sort(TREASURE_NODE_COMPARATOR);
179 
180  // Calculate the real ratio of chances (summed up to be 100%). Also
181  // attach lists to tree model.
182  for (final TreasureTreeNode realNode : tmpList) {
183  realNode.recalculateChances();
184 
185  // check for special treasurelists, which are put in sub-folders
186  final DefaultMutableTreeNode specialTreasureList = specialTreasureLists.get(realNode.getTreasureObj().getName());
187  if (specialTreasureList == null) {
188  root.add(realNode); // normal treasurelist - attach to root node
189  } else {
190  specialTreasureList.add(realNode);
191  }
192  }
193  }
194 
200  private static void linkSubLists(@NotNull final Iterable<TreasureTreeNode> needLink, @NotNull final TreasureTree treasures) {
201  final List<TreasureTreeNode> needSecondLink = new ArrayList<>();
202  linkSubList2(needLink, treasures, false, needSecondLink);
203  linkSubList2(needSecondLink, treasures, true, null); // do second linking to link all what is left
204  }
205 
206  private static void linkSubList2(@NotNull final Iterable<TreasureTreeNode> needLink, @NotNull final TreasureTree treasures, final boolean processSecondLinking, @Nullable final List<TreasureTreeNode> needSecondLink) {
207  for (final TreasureTreeNode node : needLink) {
208  final TreasureTreeNode realNode = getRealNode(treasures, node);
209  if (realNode != null) {
210  node.getTreasureObj().copyListType(realNode.getTreasureObj());
211 
212  for (int i = 0; i < realNode.getChildCount(); i++) {
213  final TreasureTreeNode ttn = (TreasureTreeNode) realNode.getChildAt(i);
214  node.add(ttn.getClone(processSecondLinking, needSecondLink));
215  }
216  }
217  }
218  }
219 
226  @Nullable
227  private static TreasureTreeNode getRealNode(@NotNull final TreasureTree treasures, @NotNull final TreasureTreeNode node) {
228  return treasures.get(node.getTreasureObj().getName());
229  }
230 
237  private static void addSpecialEntries(@NotNull final Map<String, TreasureTreeNode> specialTreasureLists, @NotNull final DefaultMutableTreeNode root) {
238  for (final MutableTreeNode folder : specialTreasureLists.values()) {
239  root.add(folder);
240  }
241  }
242 
252  private static void loadTreasureList(@NotNull final ErrorView errorView, @NotNull final File file, final Collection<TreasureTreeNode> tmpList, final List<TreasureTreeNode> needLink) {
253  final ErrorViewCollector errorViewCollector = new ErrorViewCollector(errorView, file);
254  try {
255  try (InputStream inputStream = new FileInputStream(file)) {
256  try (Reader reader = new InputStreamReader(inputStream, IOUtils.MAP_ENCODING)) {
257  try (BufferedReader bufferedReader = new BufferedReader(reader)) {
258  while (true) {
259  final String rawLine = bufferedReader.readLine();
260  if (rawLine == null) {
261  break;
262  }
263  final String line = rawLine.trim();
264  if (!line.isEmpty() && !line.startsWith("#")) {
265  if (line.startsWith("treasure")) {
266  final int i = line.indexOf(' ');
267  if (i == -1) {
268  errorViewCollector.addWarning(ErrorViewCategory.TREASURES_ENTRY_INVALID, "unexpected line: \"" + line + "\"");
269  } else {
270  final String name = line.substring(i).trim();
271  final TreasureTreeNode node = new TreasureTreeNode(new TreasureListTreasureObj(name, line.startsWith("treasureone") ? TreasureListTreasureObjType.ONE : TreasureListTreasureObjType.MULTI));
272  tmpList.add(node);
273 
274  readInsideList(errorViewCollector, node, bufferedReader, needLink);
275  }
276  } else {
277  errorViewCollector.addWarning(ErrorViewCategory.TREASURES_ENTRY_INVALID, "unexpected line: \"" + line + "\"");
278  }
279  }
280  }
281  }
282  }
283  }
284  } catch (final IOException ex) {
285  errorViewCollector.addWarning(ErrorViewCategory.TREASURES_FILE_INVALID, ex.getMessage());
286  }
287  }
288 
298  private static void loadTreasureDir(@NotNull final ErrorView errorView, @NotNull final File dir, @NotNull final List<TreasureTreeNode> tmpList, @NotNull final List<TreasureTreeNode> needLink) {
299  final String[] traverse = dir.list(TREASURE_LIST_FILTER);
300  if (traverse == null) {
301  return;
302  }
303 
304  Arrays.sort(traverse);
305  for (final String entry : traverse) {
306  final File file = new File(dir, entry);
307  if (file.isFile()) {
308  loadTreasureList(errorView, file, tmpList, needLink);
309  } else if (file.isDirectory()) {
310  loadTreasureDir(errorView, file, tmpList, needLink);
311  }
312  }
313  }
314 
324  private static void readInsideList(@NotNull final ErrorViewCollector errorViewCollector, @NotNull final TreasureTreeNode parentNode, @NotNull final BufferedReader reader, @NotNull final List<TreasureTreeNode> needLink) throws IOException {
325  TreasureTreeNode node = null;
326 
327  boolean insideArch = false;
328 
329  while (true) {
330  final String rawLine = reader.readLine();
331  if (rawLine == null) {
332  break;
333  }
334  final String line = rawLine.trim();
335  if (line.equals("end")) {
336  break;
337  }
338  if (!line.isEmpty() && !line.startsWith("#")) {
339  if (insideArch) {
340  if (line.equals("more")) {
341  insideArch = false;
342  } else if (line.startsWith("chance")) {
343  try {
344  node.getTreasureObj().setChance(Integer.parseInt(line.substring(line.indexOf(' ') + 1).trim()));
345  } catch (final NumberFormatException ignored) {
346  errorViewCollector.addWarning(ErrorViewCategory.TREASURES_ENTRY_INVALID, "list " + parentNode.getTreasureObj().getName() + ": arch " + node.getTreasureObj().getName() + " chance is not a number.");
347  }
348  } else if (line.startsWith("nrof")) {
349  try {
350  node.getTreasureObj().setNrof(Integer.parseInt(line.substring(line.indexOf(' ') + 1).trim()));
351  } catch (final NumberFormatException ignored) {
352  errorViewCollector.addWarning(ErrorViewCategory.TREASURES_ENTRY_INVALID, "list " + parentNode.getTreasureObj().getName() + ": arch " + node.getTreasureObj().getName() + " nrof value is not a number.");
353  }
354  } else if (line.startsWith("magic")) {
355  try {
356  node.getTreasureObj().setMagic(Integer.parseInt(line.substring(line.indexOf(' ') + 1).trim()));
357  } catch (final NumberFormatException ignored) {
358  errorViewCollector.addWarning(ErrorViewCategory.TREASURES_ENTRY_INVALID, "list " + parentNode.getTreasureObj().getName() + ": arch " + node.getTreasureObj().getName() + " magic value is not a number.");
359  }
360  } else if (line.startsWith("artifact_chance") || line.startsWith("title") || line.startsWith("difficulty") || line.startsWith("quality_quality") || line.startsWith("quality_range") || line.startsWith("material_quality") || line.startsWith("material_range")) {
361  // ignored for now; prevent error message when loading Daimonin/Atrinik treasure lists
362  } else if (line.startsWith("name") || line.startsWith("t_style") || line.startsWith("affinity")) {
363  // ignored for now; prevent error message when loading Atrinik treasure lists
364  } else if (line.startsWith("artifact ") || line.startsWith("list_magic_value ") || line.startsWith("list_magic_adjustment ") || line.startsWith("change_name ") || line.startsWith("change_title ") || line.startsWith("slaying ")) {
365  // ignored for now; prevent error message when loading Crossfire treasure lists
366  } else if (line.equals("no")) {
367  final int parentChance = node.getTreasureObj().getChance();
368  final int chance;
369  if (parentChance == TreasureObj.UNSET) {
370  errorViewCollector.addWarning(ErrorViewCategory.TREASURES_ENTRY_INVALID, "list " + parentNode.getTreasureObj().getName() + ": arch " + node.getTreasureObj().getName() + " has NO-list but chance is unset!");
371  chance = 0;
372  } else {
373  chance = 100 - parentChance;
374  }
375 
376  final TreasureTreeNode subNode = new TreasureTreeNode(new NoTreasureObj(chance));
377  node.add(subNode);
378 
379  readInsideList(errorViewCollector, subNode, reader, needLink);
380  } else if (line.equals("yes")) {
381  final TreasureTreeNode subNode = new TreasureTreeNode(new YesTreasureObj(node.getTreasureObj().getChance()));
382  node.add(subNode);
383  readInsideList(errorViewCollector, subNode, reader, needLink);
384  } else {
385  errorViewCollector.addWarning(ErrorViewCategory.TREASURES_ENTRY_INVALID, "list " + parentNode.getTreasureObj().getName() + ", arch " + node.getTreasureObj().getName() + ": unexpected line: \"" + line + "\"");
386  }
387  } else {
388  if (line.startsWith("arch ")) {
389  node = new TreasureTreeNode(new ArchTreasureObj(line.substring(line.indexOf(' ')).trim()));
390  parentNode.add(node);
391  insideArch = true;
392  } else if (line.startsWith("list ")) {
393  final String newName = line.substring(line.indexOf(' ')).trim();
395  parentNode.add(node);
396  needLink.add(node); // this node needs to be linked to it's content later
397 
398  // check for potential infinite loops by lists containing itself
399  if (node.getTreasureObj().isTreasureList() && parentNode.getTreasureObj().getName().equals(newName)) {
400  node.getTreasureObj().setHasLoop(true);
401  }
402 
403  insideArch = true;
404  } else {
405  errorViewCollector.addWarning(ErrorViewCategory.TREASURES_ENTRY_INVALID, parentNode + ": unknown line: \"" + line + "\"");
406  }
407  }
408  }
409  }
410  }
411 
412 }
net.sf.gridarta.model.treasurelist.TreasureObj.setMagic
void setMagic(final int magic)
Definition: TreasureObj.java:166
net.sf.gridarta.model.errorview.ErrorViewCollector.addWarning
void addWarning(@NotNull final ErrorViewCategory category)
Definition: ErrorViewCollector.java:68
net.sf.gridarta.model.treasurelist.TreasureTreeNode
Definition: TreasureTreeNode.java:33
net.sf.gridarta.model.treasurelist.TreasureLoader.readInsideList
static void readInsideList(@NotNull final ErrorViewCollector errorViewCollector, @NotNull final TreasureTreeNode parentNode, @NotNull final BufferedReader reader, @NotNull final List< TreasureTreeNode > needLink)
Definition: TreasureLoader.java:324
net.sf.gridarta.model.treasurelist.TreasureLoader
Definition: TreasureLoader.java:54
net.sf.gridarta.model.treasurelist.TreasureLoader.TREASURE_LIST_FILTER
static final FilenameFilter TREASURE_LIST_FILTER
Definition: TreasureLoader.java:80
net.sf.gridarta
net.sf.gridarta.model.treasurelist.TreasureObj.getName
String getName()
Definition: TreasureObj.java:225
net.sf.gridarta.model.treasurelist.TreasureLoader.addSpecialEntries
static void addSpecialEntries(@NotNull final Map< String, TreasureTreeNode > specialTreasureLists, @NotNull final DefaultMutableTreeNode root)
Definition: TreasureLoader.java:237
net.sf.gridarta.model.treasurelist.TreasureListTreasureObj
Definition: TreasureListTreasureObj.java:28
net.sf.gridarta.model.errorview.ErrorViewCategory.TREASURES_ENTRY_INVALID
TREASURES_ENTRY_INVALID
Definition: ErrorViewCategory.java:88
net.sf
net.sf.gridarta.model.treasurelist.TreasureObj.UNSET
static final int UNSET
Definition: TreasureObj.java:42
net.sf.gridarta.model.treasurelist.TreasureLoader.linkSubList2
static void linkSubList2(@NotNull final Iterable< TreasureTreeNode > needLink, @NotNull final TreasureTree treasures, final boolean processSecondLinking, @Nullable final List< TreasureTreeNode > needSecondLink)
Definition: TreasureLoader.java:206
net.sf.gridarta.model.treasurelist.TreasureLoader.loadTreasureDir
static void loadTreasureDir(@NotNull final ErrorView errorView, @NotNull final File dir, @NotNull final List< TreasureTreeNode > tmpList, @NotNull final List< TreasureTreeNode > needLink)
Definition: TreasureLoader.java:298
net.sf.gridarta.model.errorview.ErrorView
Definition: ErrorView.java:28
net.sf.gridarta.model.treasurelist.TreasureObj
Definition: TreasureObj.java:32
net.sf.gridarta.model.treasurelist.TreasureLoader.getRealNode
static TreasureTreeNode getRealNode(@NotNull final TreasureTree treasures, @NotNull final TreasureTreeNode node)
Definition: TreasureLoader.java:227
net.sf.gridarta.model.treasurelist.TreasureLoader.TREASURE_NODE_COMPARATOR
static final Comparator< TreasureTreeNode > TREASURE_NODE_COMPARATOR
Definition: TreasureLoader.java:67
net.sf.gridarta.model.treasurelist.TreasureLoader.linkSubLists
static void linkSubLists(@NotNull final Iterable< TreasureTreeNode > needLink, @NotNull final TreasureTree treasures)
Definition: TreasureLoader.java:200
net.sf.gridarta.model.treasurelist.TreasureLoader.parseTreasures
static TreasureTree parseTreasures(@NotNull final ErrorView errorView, @NotNull final Map< String, TreasureTreeNode > specialTreasureLists, @NotNull final ConfigSource configSource, @NotNull final ProjectSettings projectSettings)
Definition: TreasureLoader.java:114
net.sf.gridarta.model.treasurelist.TreasureObj.setHasLoop
void setHasLoop(final boolean hasLoop)
Definition: TreasureObj.java:198
net.sf.gridarta.model.treasurelist.YesTreasureObj
Definition: YesTreasureObj.java:28
net.sf.gridarta.model.configsource.ConfigSource
Definition: ConfigSource.java:37
net.sf.gridarta.model.treasurelist.TreasureObj.getChance
int getChance()
Definition: TreasureObj.java:139
net.sf.gridarta.model.treasurelist.TreasureLoader.createTreasureTree
static TreasureTree createTreasureTree(@NotNull final List< TreasureTreeNode > tmpList, @NotNull final Iterable< TreasureTreeNode > needLink, @NotNull final Map< String, TreasureTreeNode > specialTreasureLists)
Definition: TreasureLoader.java:159
net.sf.gridarta.model.treasurelist.TreasureObj.setChance
void setChance(final int value)
Definition: TreasureObj.java:131
net
net.sf.gridarta.model.errorview
Definition: ErrorView.java:20
net.sf.gridarta.model.errorview.ErrorViewCollector
Definition: ErrorViewCollector.java:31
net.sf.gridarta.model.treasurelist.TreasureLoader.LOG
static final Category LOG
Definition: TreasureLoader.java:60
net.sf.gridarta.model.treasurelist.TreasureLoader.TreasureLoader
TreasureLoader()
Definition: TreasureLoader.java:102
net.sf.gridarta.model.errorview.ErrorViewCategory
Definition: ErrorViewCategory.java:28
net.sf.gridarta.model.treasurelist.TreasureListTreasureObjType.ONE
ONE
Definition: TreasureListTreasureObjType.java:36
net.sf.gridarta.model.treasurelist.TreasureTree
Definition: TreasureTree.java:32
net.sf.gridarta.model.treasurelist.ArchTreasureObj
Definition: ArchTreasureObj.java:28
net.sf.gridarta.model.treasurelist.TreasureTreeNode.getClone
MutableTreeNode getClone(final boolean processSecondLinking, @Nullable final List< TreasureTreeNode > needSecondLink)
Definition: TreasureTreeNode.java:59
net.sf.gridarta.model.treasurelist.NoTreasureObj
Definition: NoTreasureObj.java:28
net.sf.gridarta.model.configsource
Definition: AbstractConfigSource.java:20
net.sf.gridarta.model.settings.ProjectSettings
Definition: ProjectSettings.java:29
net.sf.gridarta.model.treasurelist.TreasureLoader.addTopLevelEntries
static void addTopLevelEntries(@NotNull final List< TreasureTreeNode > tmpList, @NotNull final Map< String, TreasureTreeNode > specialTreasureLists, @NotNull final DefaultMutableTreeNode root)
Definition: TreasureLoader.java:177
net.sf.gridarta.model.treasurelist.TreasureObj.setNrof
void setNrof(final int nrof)
Definition: TreasureObj.java:182
net.sf.gridarta.model.treasurelist.TreasureListTreasureObjType
Definition: TreasureListTreasureObjType.java:26
net.sf.gridarta.model
net.sf.gridarta.model.errorview.ErrorViewCategory.TREASURES_FILE_INVALID
TREASURES_FILE_INVALID
Definition: ErrorViewCategory.java:90
net.sf.gridarta.model.treasurelist.TreasureObj.isTreasureList
final boolean isTreasureList
Definition: TreasureObj.java:55
net.sf.gridarta.utils.IOUtils
Definition: IOUtils.java:40
net.sf.gridarta.model.treasurelist.TreasureListTreasureObjType.MULTI
MULTI
Definition: TreasureListTreasureObjType.java:31
net.sf.gridarta.model.treasurelist.TreasureTreeNode.getTreasureObj
TreasureObj getTreasureObj()
Definition: TreasureTreeNode.java:100
net.sf.gridarta.model.treasurelist.TreasureLoader.loadTreasureList
static void loadTreasureList(@NotNull final ErrorView errorView, @NotNull final File file, final Collection< TreasureTreeNode > tmpList, final List< TreasureTreeNode > needLink)
Definition: TreasureLoader.java:252
net.sf.gridarta.utils
Definition: ActionBuilderUtils.java:20
net.sf.gridarta.model.settings
Definition: AbstractDefaultProjectSettings.java:20
net.sf.gridarta.utils.IOUtils.MAP_ENCODING
static final String MAP_ENCODING
Definition: IOUtils.java:52
net.sf.gridarta.model.treasurelist.TreasureTree.putAll
void putAll(final Iterable< TreasureTreeNode > treasureTreeNodes)
Definition: TreasureTree.java:76