Gridarta Editor
AbstractGameObjectParser.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.io;
21 
22 import java.awt.Point;
23 import java.io.BufferedReader;
24 import java.io.IOException;
25 import java.util.ArrayList;
26 import java.util.Collection;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
37 import org.apache.log4j.Category;
38 import org.apache.log4j.Logger;
39 import org.jetbrains.annotations.NotNull;
40 import org.jetbrains.annotations.Nullable;
41 
49 public abstract class AbstractGameObjectParser<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> implements GameObjectParser<G, A, R> {
50 
54  private static final Category LOG = Logger.getLogger(AbstractGameObjectParser.class);
55 
59  @NotNull
61 
65  @NotNull
67 
74  protected AbstractGameObjectParser(@NotNull final GameObjectFactory<G, A, R> gameObjectFactory, @NotNull final ArchetypeSet<G, A, R> archetypeSet) {
75  this.gameObjectFactory = gameObjectFactory;
76  this.archetypeSet = archetypeSet;
77  }
78 
79  @Nullable
80  @Override
81  public G load(@NotNull final BufferedReader reader, @Nullable final Collection<G> objects) throws IOException {
82  final String line = reader.readLine();
83  if (line == null) {
84  return null;
85  }
86 
87  return load(reader, line, objects);
88  }
89 
94  @Nullable
95  @Override
96  public G load(@NotNull final BufferedReader reader, @NotNull final String firstLine, @Nullable final Collection<G> objects) throws IOException {
97  final String archName = readArchName(reader, firstLine);
98  if (archName == null) {
99  return null;
100  }
101 
102  final R archetype = archetypeSet.getOrCreateArchetype(archName);
103  final G gameObject = gameObjectFactory.createGameObject(archetype);
104 
105  while (true) {
106  final String thisLine2 = reader.readLine();
107  if (thisLine2 == null) {
108  throw new IOException("unexpected end of file while reading 'arch " + archName + "'");
109  }
110  final String thisLine = thisLine2.trim();
111 
112  if (thisLine.startsWith("arch ")) {
113  final G invItem = load(reader, thisLine, objects);
114  assert invItem != null : "invItem must not be null here.";
115  gameObject.addLast(invItem);
116  } else if (thisLine.equals("end")) {
117  if (objects != null) {
118  objects.add(gameObject);
119  }
120  return gameObject;
121  } else if (thisLine.equals("msg")) {
122  gameObject.setMsgText("");
123  while (true) {
124  final String thisLine3 = reader.readLine();
125  if (thisLine3 == null) {
126  throw new IOException("unexpected end of file while reading 'arch " + archName + "'");
127  }
128 
129  if (thisLine3.trim().equals("endmsg")) {
130  break;
131  }
132 
133  gameObject.addMsgTextLine(thisLine3);
134  }
135  } else if (parseLine(thisLine, gameObject)) {
136  // ignore
137  } else if (thisLine.startsWith("x ")) {
138  gameObject.setMapX(Integer.parseInt(thisLine.substring(2)));
139  } else if (thisLine.startsWith("y ")) {
140  gameObject.setMapY(Integer.parseInt(thisLine.substring(2)));
141  } else {
142  gameObject.addObjectText(thisLine);
143  }
144  }
145  }
146 
155  protected boolean parseLine(@NotNull final String line, @NotNull final G gameObject) {
156  return false;
157  }
158 
168  @Nullable
169  private static String readArchName(@NotNull final BufferedReader reader, @NotNull final String firstLine) throws IOException {
170  for (String thisLine2 = firstLine; thisLine2 != null; thisLine2 = reader.readLine()) {
171  final String thisLine = thisLine2.trim();
172  if (thisLine.startsWith("arch ")) {
173  return thisLine.substring(5).trim();
174  } else if (!thisLine.isEmpty() && !thisLine.startsWith("#")) {
175  throw new IOException("unexpected first line of game object: '" + thisLine + "', expected 'arch ...'");
176  }
177  }
178  return null;
179  }
180 
181  @Override
182  public void collectTempList(@NotNull final List<G> objects) {
183  final Collection<G> tailList = new ArrayList<>();
184 
185  for (final G gameObject : objects) {
186  if (!gameObject.isInContainer()) {
187  expandMulti(gameObject, tailList);
188  }
189  }
190 
191  objects.addAll(tailList);
192  }
193 
194  @Override
195  public void save(@NotNull final Appendable appendable, @NotNull final G gameObject) throws IOException {
196  final Map<String, String> fields = getModifiedFields(gameObject);
197 
198  appendable.append("arch ");
199  appendable.append(gameObject.getArchetype().getArchetypeName());
200  appendable.append("\n");
201  for (final Entry<String, String> entry : fields.entrySet()) {
202  final String key = entry.getKey();
203  appendable.append(key);
204  if (key.equals("msg")) {
205  appendable.append("\n");
206  appendable.append(entry.getValue());
207  appendable.append("endmsg\n");
208  } else {
209  appendable.append(" ");
210  appendable.append(entry.getValue());
211  appendable.append("\n");
212  }
213  }
214 
215  for (final G inventoryItem : gameObject) {
216  save(appendable, inventoryItem);
217  }
218 
219  appendable.append("end\n");
220  }
221 
222  @Override
223  public void addModifiedFields(@NotNull final G gameObject, @NotNull final Map<String, String> fields) {
224  final BaseObject<G, A, R, ?> archetype = gameObject.getArchetype();
225 
226  final String msgText = gameObject.getMsgText();
227  final String archMsgText = archetype.getMsgText();
228  if (msgText != null && !msgText.equals(archMsgText == null ? "" : archMsgText)) {
229  fields.put("msg", msgText);
230  }
231 
232  final CharSequence objText = gameObject.getObjectText();
233  if (objText.length() != 0) {
234  for (final String aTmp : StringUtils.PATTERN_END_OF_LINE.split(objText, 0)) {
235  final String[] line = StringUtils.PATTERN_SPACES.split(aTmp, 2);
236  if (line.length == 2) {
237  fields.put(line[0], line[1]);
238  } else {
239  LOG.warn("writeMapArch: ignoring invalid arch line: " + aTmp);
240  }
241  }
242  }
243 
244  // map coordinates only belong into map arches (not inventory arches)
245  if (gameObject.getMapX() != 0) {
246  fields.put("x", Integer.toString(gameObject.getMapX()));
247  }
248  if (gameObject.getMapY() != 0) {
249  fields.put("y", Integer.toString(gameObject.getMapY()));
250  }
251  }
252 
264  private void expandMulti(@NotNull final G gameObject, @NotNull final Collection<G> objects) {
265  final Archetype<G, A, R> archetype = gameObject.getArchetype();
266 
267  if (!archetype.isMulti() || gameObject.getMultiRefCount() > 1) {
268  return;
269  }
270 
271  if (gameObject.isInContainer()) {
272  LOG.warn("Multi-part expansion for a GameObject inside a container requested.");
273  return;
274  }
275 
276  // do insertion for all non-head parts of the multi
277  final Point pos = gameObject.getMapLocation();
278  for (R oldPart = archetype.getMultiNext(); oldPart != null; oldPart = oldPart.getMultiNext()) {
279  final G tailGameObject = gameObjectFactory.createGameObjectPart(oldPart, gameObject);
280  tailGameObject.setMapX(pos.x + oldPart.getMultiX());
281  tailGameObject.setMapY(pos.y + oldPart.getMultiY());
282  objects.add(tailGameObject);
283  }
284  }
285 
286 }
G createGameObject(@NotNull R archetype)
Creates a new GameObject from an Archetype.
Utility class for string manipulation.
T getMultiNext()
Returns the next of this multi-part object.
R getOrCreateArchetype(@NotNull String archetypeName)
Returns an archetype by its name.
Interface for classes that read or write GameObject instances.
void addModifiedFields(@NotNull final G gameObject, @NotNull final Map< String, String > fields)
static final Pattern PATTERN_SPACES
The pattern that matches a non-empty sequence of spaces.
Map< String, String > getModifiedFields( @NotNull G gameObject)
Returns the modified fields of a GameObject.
final ArchetypeSet< G, A, R > archetypeSet
The ArchetypeSet for looking up archetypes.
void save(@NotNull final Appendable appendable, @NotNull final G gameObject)
Base package of all Gridarta classes.
void collectTempList(@NotNull final List< G > objects)
Reflects a game object (object on a map).
Definition: GameObject.java:36
AbstractGameObjectParser(@NotNull final GameObjectFactory< G, A, R > gameObjectFactory, @NotNull final ArchetypeSet< G, A, R > archetypeSet)
Create a new instance.
Abstract factory for creating GameObject instances.
G load(@NotNull final BufferedReader reader, @NotNull final String firstLine, @Nullable final Collection< G > objects)
GameObjects are the objects based on Archetypes found on maps.
String getMsgText()
Returns the message bound to this object.
R getArchetype()
Returns the Archetype this GameObject is based on.
static final Pattern PATTERN_END_OF_LINE
The pattern to match end of line characters separating lines.
static String readArchName(@NotNull final BufferedReader reader, @NotNull final String firstLine)
Read an archetype name.
boolean parseLine(@NotNull final String line, @NotNull final G gameObject)
Parse one line.
Interface that captures similarities between different ArchetypeSet implementations.
G load(@NotNull final BufferedReader reader, @Nullable final Collection< G > objects)
boolean isMulti()
Returns whether this Archetype is a multi-part object.
final GameObjectFactory< G, A, R > gameObjectFactory
The game object factory for creating new game object instances.
static final Category LOG
The Logger for printing log messages.
Abstract base class for classes implementing GameObjectParsers.
void expandMulti(@NotNull final G gameObject, @NotNull final Collection< G > objects)
If the given gameObject is a multi-part head, we generate the appropriate tail (from the archetype st...
G createGameObjectPart(@NotNull R archetype, @Nullable G head)
Creates a new GameObject from an Archetype.