Gridarta Editor
ArtifactParser.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.artifact;
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.InputStreamReader;
28 import java.util.ArrayList;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.List;
42 import org.apache.log4j.Category;
43 import org.apache.log4j.Logger;
44 import org.jetbrains.annotations.NotNull;
45 import org.jetbrains.annotations.Nullable;
46 
51 public class ArtifactParser<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> {
52 
56  @NotNull
57  private static final Category LOG = Logger.getLogger(ArtifactParser.class);
58 
62  @NotNull
64 
68  @NotNull
69  private final ErrorView errorView;
70 
74  @NotNull
76 
80  @NotNull
81  private final List<G> invObjects = new ArrayList<>();
82 
86  @NotNull
87  private final FilenameFilter artifactFilenameFilter = new FilenameFilter() {
88 
89  @Override
90  public boolean accept(final File dir, final String name) {
91  final File fullPath = new File(dir, name);
92  // TODO: Replace this with a proper FileFilter.
93  return fullPath.isDirectory() && !name.equalsIgnoreCase("cvs") && !name.equalsIgnoreCase(".xvpics") && !name.equalsIgnoreCase(".svn") || name.toLowerCase().endsWith(".art");
94  }
95 
96  };
97 
104  public ArtifactParser(@NotNull final ArchetypeSet<G, A, R> archetypeSet, @NotNull final ErrorView errorView, @NotNull final AbstractArchetypeParser<G, A, R, ?> archetypeParser) {
105  this.archetypeSet = archetypeSet;
106  this.errorView = errorView;
107  this.archetypeParser = archetypeParser;
108  }
109 
114  @NotNull
115  public Collection<G> getInvObjects() {
116  return Collections.unmodifiableCollection(invObjects);
117  }
118 
129  public void loadArchesFromArtifacts(@NotNull final File f, @NotNull final String panelName, @NotNull final String folderName) {
130  final int archetypes = archetypeSet.getArchetypeCount();
131  loadArchesFromArtifactsRecursive(f, "", panelName, folderName);
132  if (LOG.isInfoEnabled()) {
133  LOG.info("Loaded " + (archetypeSet.getArchetypeCount() - archetypes) + " artifacts from '" + f + "'.");
134  }
135  }
136 
148  private void loadArchesFromArtifactsRecursive(@NotNull final File f, @NotNull final String archPath, @NotNull final String panelName, @NotNull final String folderName) {
149  if (f.isDirectory()) {
150  final String[] traverse = f.list(artifactFilenameFilter);
151  if (traverse != null) {
152  Arrays.sort(traverse);
153  for (final String entry : traverse) {
154  loadArchesFromArtifactsRecursive(new File(f, entry), archPath + "/" + entry, panelName, folderName);
155  }
156  }
157  } else {
158  loadArtifact(new ErrorViewCollector(errorView, f), f, archPath, panelName, folderName);
159  }
160  }
161 
172  private void loadArtifact(@NotNull final ErrorViewCollector errorViewCollector, @NotNull final File f, @NotNull final String archPath, @NotNull final String panelName, @NotNull final String folderName) {
173  try {
174  try (FileInputStream fis = new FileInputStream(f)) {
175  try (InputStreamReader isr = new InputStreamReader(fis)) {
176  try (BufferedReader in = new BufferedReader(isr)) {
177  loadArtifact(in, errorViewCollector, archPath, panelName, folderName);
178  }
179  }
180  }
181  } catch (final IOException ex) {
182  errorViewCollector.addWarning(ErrorViewCategory.ARTIFACT_FILE_INVALID, ex.getMessage());
183  }
184  }
185 
195  public void loadArtifact(@NotNull final BufferedReader in, @NotNull final ErrorViewCollector errorViewCollector, @NotNull final String archPath, @NotNull final String panelName, @NotNull final String folderName) throws IOException {
196  int editorCode = -1;
197  @Nullable String name = null;
198  @Nullable String defArchName = null;
199  int lineCount = 0;
200 
201  @Nullable String editorPath = null;
202  while (true) {
203  final String thisLine2 = in.readLine();
204  if (thisLine2 == null) {
205  break;
206  }
207  lineCount++;
208  final String thisLine = thisLine2.trim();
209  // ignore white space lines or '#' comment lines
210  if (!thisLine.startsWith("#") && !thisLine.isEmpty()) {
211  if (thisLine.startsWith("artifact ")) {
212  name = thisLine.substring(9);
213  } else if (thisLine.startsWith("def_arch ")) {
214  defArchName = thisLine.substring(9);
215  } else if (thisLine.startsWith("editor ")) {
216  final String editor = thisLine.substring(7);
217  final int tIndex = editor.indexOf(':');
218  if (tIndex == -1) {
219  editorCode = Integer.parseInt(editor);
220  } else {
221  editorPath = editor.substring(tIndex + 1);
222  // TODO: use editorPath for determining the place of this GameObject
223  editorCode = Integer.parseInt(editor.substring(0, tIndex));
224  }
225  } else if (thisLine.startsWith("Object")) {
226  if (editorCode == -1) {
227  // TOOD: Warn user
228  }
229  if (editorCode == 0 || editorCode == 2/* || editorCode == -1 */) {
230  // TODO: read until "end"
231  }
232  final String objTitle = thisLine.length() > 7 ? thisLine.substring(7) : "";
233  // TODO: Allow not having a def arch
234  // at this point we MUST have a legal name and def arch
235  if (defArchName == null) {
236  errorViewCollector.addWarning(ErrorViewCategory.ARTIFACT_ENTRY_INVALID, "line " + lineCount + ", Object '" + defArchName + "' / '" + name + "' / '" + objTitle + "' has missing def_arch");
237  } else if (defArchName.isEmpty()) {
238  errorViewCollector.addWarning(ErrorViewCategory.ARTIFACT_ENTRY_INVALID, "line " + lineCount + ", Object '" + defArchName + "' / '" + name + "' / '" + objTitle + "' has empty def_arch");
239  } else if (name == null) {
240  errorViewCollector.addWarning(ErrorViewCategory.ARTIFACT_ENTRY_INVALID, "line " + lineCount + ", Object '" + defArchName + "' / '" + name + "' / '" + objTitle + "' has missing name");
241  } else if (name.isEmpty()) {
242  errorViewCollector.addWarning(ErrorViewCategory.ARTIFACT_ENTRY_INVALID, "line " + lineCount + ", Object '" + defArchName + "' / '" + name + "' / '" + objTitle + "' has empty name");
243  } else {
244  try {
245  final R archetype = archetypeSet.getArchetype(defArchName);
246  if (editorCode != 0 && editorCode != 2) { // the next line of our file is part of a arch parse until a "end" comes
247  // now the editor will do the same as the real server:
248  // get the default arch as base and parse the new values over it
249  // the extended functions of the artifacts file can be ignored here.
250  archetypeParser.parseArchetypeFromStream(in, editorCode == 2 ? null : archetype, thisLine, name, panelName, folderName, archPath, invObjects, errorViewCollector); // XXX: editorCode == 2 looks incorrect due to the enclosing condition editorCode != 2
251  // note: in the parser is a small part where we handle the title setting
252  // and the reverse type setting for type == -1 (unique items marker in artifacts file)
253  } else {
254  while (true) {
255  final String thisLine3 = in.readLine();
256  if (thisLine3 == null || thisLine3.equals("end")) {
257  break;
258  }
259  }
260  }
261  } catch (final UndefinedArchetypeException ex) {
262  errorViewCollector.addWarning(ErrorViewCategory.ARTIFACT_ENTRY_INVALID, "line " + lineCount + ", Object '" + defArchName + "' / '" + name + "' / '" + objTitle + "' references undefined archetype '" + ex.getMessage() + "'");
263  }
264  }
265  name = null;
266  defArchName = null;
267  editorPath = null;
268  editorCode = -1;
269  }
270  }
271  }
272  }
273 
274 }
final ErrorView errorView
The ErrorView for reporting errors.
Convenience class for adding messages to a ErrorView instance using a fixed category name...
Reading and writing of maps, handling of paths.
void loadArtifact(@NotNull final BufferedReader in, @NotNull final ErrorViewCollector errorViewCollector, @NotNull final String archPath, @NotNull final String panelName, @NotNull final String folderName)
Loads one artifact.
void loadArchesFromArtifactsRecursive(@NotNull final File f, @NotNull final String archPath, @NotNull final String panelName, @NotNull final String folderName)
This method takes a filename or directory name as argument.
final List< G > invObjects
Collects all inventory objects.
Defines possible error categories for ErrorView instances.
final ArchetypeSet< G, A, R > archetypeSet
The ArchetypeSet for looking up archetypes.
Interface for classes displaying error messages.
Definition: ErrorView.java:28
Base package of all Gridarta classes.
final AbstractArchetypeParser< G, A, R, ?> archetypeParser
The AbstractArchetypeParser to use.
Reflects a game object (object on a map).
Definition: GameObject.java:36
int getArchetypeCount()
Returns the number of Archetypes available.
R getArchetype(@NotNull String archetypeName)
Returns an Archetype by its name.
Collection< G > getInvObjects()
Returns all collected inventory objects.
void loadArtifact(@NotNull final ErrorViewCollector errorViewCollector, @NotNull final File f, @NotNull final String archPath, @NotNull final String panelName, @NotNull final String folderName)
Loads one artifact.
static final Category LOG
The logger for printing log messages.
GameObjects are the objects based on Archetypes found on maps.
void parseArchetypeFromStream(@NotNull final BufferedReader in, @Nullable final R prototype, @Nullable final String line, @Nullable final String archName, @NotNull final String panelName, @NotNull final String folderName, @NotNull final String archPath, @NotNull final List< G > invObjects, @NotNull final ErrorViewCollector errorViewCollector)
void loadArchesFromArtifacts(@NotNull final File f, @NotNull final String panelName, @NotNull final String folderName)
This method takes a filename or directory name as argument.
ArtifactParser(@NotNull final ArchetypeSet< G, A, R > archetypeSet, @NotNull final ErrorView errorView, @NotNull final AbstractArchetypeParser< G, A, R, ?> archetypeParser)
Creates a new instance.
Interface that captures similarities between different ArchetypeSet implementations.
Abstract base implementation of ArchetypeParser.
Parser for artifact definitions.
final FilenameFilter artifactFilenameFilter
A FilenameFilter that matches "*.art" files.