Gridarta Editor
AttachTiledMaps.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.actions;
21 
22 import java.io.File;
23 import java.io.IOException;
24 import java.util.ArrayList;
25 import java.util.Collection;
26 import java.util.List;
27 import java.util.regex.Matcher;
40 import net.sf.gridarta.utils.Size2D;
41 import org.jetbrains.annotations.NotNull;
42 import org.jetbrains.annotations.Nullable;
43 
50 public class AttachTiledMaps<G extends GameObject<G, A, R>, A extends MapArchObject<A>, R extends Archetype<G, A, R>> {
51 
55  @NotNull
57 
61  @NotNull
62  private final TileLink @NotNull [] tileLinks;
63 
69  public AttachTiledMaps(@NotNull final MapManager<G, A, R> mapManager, @NotNull final TileLink @NotNull [] tileLinks) {
70  this.mapManager = mapManager;
71  this.tileLinks = tileLinks.clone();
72  }
73 
85  public void attachTiledMaps(@NotNull final MapModel<G, A, R> mapModel, @NotNull final String @NotNull [] tilePaths, @NotNull final File mapsDirectory, final boolean performAction) throws CannotLoadMapFileException, CannotSaveMapFileException, MapSizeMismatchException, UnsavedMapException {
86  if (mapModel.getMapFile() == null) {
87  throw new UnsavedMapException(); // cannot update unsaved map
88  }
89 
90  if (performAction) {
91  final List<MapControl<G, A, R>> mapControls = new ArrayList<>(tilePaths.length);
92  try {
93  // first action: we go around all links and try to load the maps
94  loadAdjacentMaps(mapModel, mapControls, tilePaths);
95 
96  // We have loaded all direct linked maps around our map.
97  // now, lets check free spaces.
98  fillAdjacentMaps(mapControls);
99 
100  validateMapSizes(mapModel, mapControls);
101 
102  // finally... set the links!
103  updateTilePaths(mapModel, mapControls, tilePaths, mapsDirectory);
104 
105  // all done! now we write all back
106  saveAdjacentMaps(mapControls);
107  } finally {
108  for (final MapControl<G, A, R> mapControl : mapControls) {
109  if (mapControl != null) {
110  mapManager.release(mapControl);
111  }
112  }
113  }
114  }
115  }
116 
125  private void loadAdjacentMaps(@NotNull final MapModel<G, A, R> mapModel, @NotNull final Collection<MapControl<G, A, R>> mapControls, @NotNull final String @NotNull [] tilePaths) throws CannotLoadMapFileException {
126  for (@Nullable final String tilePath : tilePaths) {
127  @Nullable final MapControl<G, A, R> mapControl;
128  if (tilePath != null && !tilePath.isEmpty()) {
129  try {
130  mapControl = loadMapControl(mapModel, tilePath);
131  } catch (final IOException ex) {
132  throw new CannotLoadMapFileException(tilePath, ex);
133  }
134  } else {
135  mapControl = null;
136  }
137  mapControls.add(mapControl);
138  }
139  }
140 
147  private void saveAdjacentMaps(@NotNull final Iterable<MapControl<G, A, R>> mapControls) throws CannotSaveMapFileException {
148  for (final MapControl<G, A, R> mapControl : mapControls) {
149  if (mapControl != null) {
150  try {
151  if (mapControl.getMapModel().isModified()) {
152  mapControl.save();
153  }
154  } catch (final IOException e) {
155  final MapFile mapFile = mapControl.getMapModel().getMapFile();
156  assert mapFile != null;
157  throw new CannotSaveMapFileException(mapFile.getFile(), e);
158  }
159  }
160  }
161  }
162 
168  private void fillAdjacentMaps(@NotNull final List<MapControl<G, A, R>> mapControls) throws CannotLoadMapFileException {
169  boolean repeatFlag = true;
170  while (repeatFlag) {
171  repeatFlag = false;
172  for (int i = 0; i < mapControls.size(); i++) {
173  final MapControl<G, A, R> mapControl = mapControls.get(i);
174  if (mapControl != null) {
175  final TileLink tileLink = tileLinks[i];
176  final MapLink[] mapLinks = tileLink.getMapLinks();
177  for (final MapLink mapLink : mapLinks) {
178  final Direction direction = mapLink.getMapDirection();
179  if (mapControls.get(direction.ordinal()) == null) {
180  final String tilePath = mapControl.getMapModel().getMapArchObject().getTilePath(mapLink.getLinkDirection());
181  if (!tilePath.isEmpty()) {
182  try {
183  mapControls.set(direction.ordinal(), loadMapControl(mapControl.getMapModel(), tilePath));
184  repeatFlag = true;
185  } catch (final IOException ex) {
186  throw new CannotLoadMapFileException(tilePath, ex);
187  }
188  }
189  }
190  }
191  }
192  }
193  }
194  }
195 
204  private void validateMapSizes(@NotNull final MapModel<G, A, R> mapModel, @NotNull final Iterable<MapControl<G, A, R>> mapControls) throws MapSizeMismatchException {
205  final Size2D mapSize = mapModel.getMapArchObject().getMapSize();
206  for (final MapControl<G, A, R> mapControl : mapControls) {
207  if (mapControl != null) {
208  final MapModel<G, A, R> tmpMapModel = mapControl.getMapModel();
209  final MapArchObject<A> otherMap = tmpMapModel.getMapArchObject();
210  final Size2D otherMapSize = otherMap.getMapSize();
211  if (!mapSize.equals(otherMapSize)) {
212  throw new MapSizeMismatchException(tmpMapModel.getMapFile().getFile(), mapSize, otherMapSize);
213  }
214  for (int ii = 0; ii < 2; ii++) {
215  // TODO: check links
216  }
217  }
218  }
219  }
220 
229  private void updateTilePaths(@NotNull final MapModel<G, A, R> mapModel, @NotNull final List<MapControl<G, A, R>> mapControls, @NotNull final String @NotNull [] tilePaths, @NotNull final File mapsDirectory) throws CannotSaveMapFileException {
230  for (int i = 0; i < tilePaths.length; i++) {
231  final MapControl<G, A, R> mapControl = mapControls.get(i);
232  if (mapControl != null) {
233  // generate a valid path relative to both map positions
234  final String canonicalMapPath2 = mapControl.getMapModel().getMapFile().getFile().getPath();
235  final String canonicalMapPath1 = mapModel.getMapFile().getFile().getPath();
236  final String link1 = getTilePath(canonicalMapPath1, canonicalMapPath2, mapsDirectory);
237  // set the link of our source map to the map around
238  tilePaths[i] = link1;
239  // generate again a valid path relative to both map positions
240  final String link2 = getTilePath(canonicalMapPath2, canonicalMapPath1, mapsDirectory);
241  final MapModel<G, A, R> mapModel2 = mapControl.getMapModel();
242  mapModel2.beginTransaction("Attach maps");
243  try {
244  final MapArchObject<A> mapArchObject2 = mapModel2.getMapArchObject();
245  mapArchObject2.beginTransaction();
246  try {
247  mapArchObject2.setTilePath(tileLinks[i].getRevLink(), link2 == null ? "" : link2);
248  } finally {
249  mapArchObject2.endTransaction();
250  }
251  } finally {
252  mapModel2.endTransaction();
253  }
254  }
255  }
256  }
257 
265  @Nullable
266  private MapControl<G, A, R> loadMapControl(@NotNull final MapModel<G, A, R> mapModel, @NotNull final String path) throws IOException {
267  final MapFile mapFile = mapModel.getMapFile();
268  if (mapFile == null) {
269  // unsaved map: should not happen => ignore
270  return null;
271  }
272  return mapManager.openMapFile(new MapFile(mapFile, MapPathUtils.newMapPath(path)), false);
273  }
274 
283  @Nullable
284  private static String getTilePath(@NotNull final String base, @NotNull final String link, @NotNull final File mapsDirectory) throws CannotSaveMapFileException {
285  final int pos = getLastSlashIndex(base);
286  final int pos2 = getLastSlashIndex(link);
287  final CharSequence mapDirectory;
288  try {
289  mapDirectory = mapsDirectory.getCanonicalPath();
290  } catch (final IOException e) {
291  throw new CannotSaveMapFileException(mapsDirectory, e);
292  }
293  final String first = base.substring(mapDirectory.length(), pos).trim();
294  final String second = link.substring(mapDirectory.length(), pos2).trim();
295 
296  final String sep = Matcher.quoteReplacement(File.separator);
297 
298  /* our map is in root - second is higher or same map */
299  if (first.isEmpty()) {
300  return link.substring(mapDirectory.length()).trim().replaceAll(sep, "/");
301  }
302  // same folder... we return the name without '/'
303  if (first.compareTo(second) == 0) {
304  return link.substring(pos2 + 1).trim().replaceAll(sep, "/");
305  }
306  // second is sub-folder of first
307  if (second.startsWith(first)) {
308  return link.substring(pos + 1).trim().replaceAll(sep, "/");
309  }
310  // in any other case we return a absolute path
311  return link.substring(mapDirectory.length()).trim().replaceAll(sep, "/");
312  }
313 
320  private static int getLastSlashIndex(@NotNull final String base) {
321  return Math.max(base.lastIndexOf(File.separator), base.lastIndexOf('/'));
322  }
323 
324 }
net.sf.gridarta.model.direction.Direction
A direction.
Definition: Direction.java:28
net.sf.gridarta.model.mapmanager
Definition: AbstractMapManager.java:20
net.sf.gridarta.model.mapmodel.MapModel
A MapModel reflects the data of a map.
Definition: MapModel.java:75
net.sf.gridarta.model.mapmodel.MapModel.getMapArchObject
A getMapArchObject()
Returns the Map Arch Object with the meta information about the map.
net.sf.gridarta.model.mapmanager.MapManager
A MapManager manages all opened maps.
Definition: MapManager.java:37
net.sf.gridarta
Base package of all Gridarta classes.
net.sf.gridarta.model.mapmodel.MapModel.endTransaction
void endTransaction()
End a transaction.
net.sf.gridarta.actions.AttachTiledMaps.fillAdjacentMaps
void fillAdjacentMaps(@NotNull final List< MapControl< G, A, R >> mapControls)
Fills missing adjacent map slots.
Definition: AttachTiledMaps.java:168
net.sf.gridarta.actions.AttachTiledMaps.getTilePath
static String getTilePath(@NotNull final String base, @NotNull final String link, @NotNull final File mapsDirectory)
Returns the map path for a tile of a map and a tile path.
Definition: AttachTiledMaps.java:284
net.sf
net.sf.gridarta.model.mapmodel.MapModel.beginTransaction
void beginTransaction(@NotNull String name)
Starts a new transaction.
net.sf.gridarta.model.mapmodel
Definition: AboveFloorInsertionMode.java:20
net.sf.gridarta.model.maparchobject.MapArchObject.beginTransaction
void beginTransaction()
Starts a new transaction.
net.sf.gridarta.model.mapmodel.MapPathUtils.newMapPath
static MapPath newMapPath(@NotNull final String string)
Creates a MapPath instance from string representation.
Definition: MapPathUtils.java:43
net.sf.gridarta.model.archetype
Definition: AbstractArchetype.java:20
net.sf.gridarta.model.gameobject.GameObject
Reflects a game object (object on a map).
Definition: GameObject.java:36
net.sf.gridarta.model.mapmodel.MapPathUtils
Utility class for MapPath related functions.
Definition: MapPathUtils.java:28
net.sf.gridarta.model.mapcontrol
Definition: DefaultMapControl.java:20
net.sf.gridarta.actions.AttachTiledMaps.saveAdjacentMaps
void saveAdjacentMaps(@NotNull final Iterable< MapControl< G, A, R >> mapControls)
Saves adjacent maps.
Definition: AttachTiledMaps.java:147
net.sf.gridarta.actions.AttachTiledMaps.updateTilePaths
void updateTilePaths(@NotNull final MapModel< G, A, R > mapModel, @NotNull final List< MapControl< G, A, R >> mapControls, @NotNull final String @NotNull[] tilePaths, @NotNull final File mapsDirectory)
Updates map tile paths to match the loaded maps.
Definition: AttachTiledMaps.java:229
net.sf.gridarta.model.maparchobject.MapArchObject.getMapSize
Size2D getMapSize()
Returns the map size.
net.sf.gridarta.model.gameobject
GameObjects are the objects based on Archetypes found on maps.
Definition: AbstractGameObject.java:20
net
net.sf.gridarta.actions.AttachTiledMaps.validateMapSizes
void validateMapSizes(@NotNull final MapModel< G, A, R > mapModel, @NotNull final Iterable< MapControl< G, A, R >> mapControls)
Validates all links to check that attached maps have matching width/height.
Definition: AttachTiledMaps.java:204
net.sf.gridarta.actions.AttachTiledMaps
Attaches maps to adjacent tiled maps.
Definition: AttachTiledMaps.java:50
net.sf.gridarta.model.maparchobject.MapArchObject
Interface for MapArchObjects.
Definition: MapArchObject.java:40
net.sf.gridarta.actions.AttachTiledMaps.getLastSlashIndex
static int getLastSlashIndex(@NotNull final String base)
Returns the index of the last '/' or File#separator within a string.
Definition: AttachTiledMaps.java:320
net.sf.gridarta.model.mapmodel.MapFile.getFile
File getFile()
Returns a File for this map file.
Definition: MapFile.java:102
net.sf.gridarta.model.mapmanager.MapManager.openMapFile
MapControl< G, A, R > openMapFile(@NotNull MapFile mapFile, boolean interactive)
Loads a map file.
net.sf.gridarta.actions.AttachTiledMaps.loadAdjacentMaps
void loadAdjacentMaps(@NotNull final MapModel< G, A, R > mapModel, @NotNull final Collection< MapControl< G, A, R >> mapControls, @NotNull final String @NotNull[] tilePaths)
Loads adjacent map files by "filling" them by checking the "side" path links of the loaded ones.
Definition: AttachTiledMaps.java:125
net.sf.gridarta.model.mapmodel.UnsavedMapException
Exception thrown if an operation is attempted on an unsaved map.
Definition: UnsavedMapException.java:26
net.sf.gridarta.actions.AttachTiledMaps.attachTiledMaps
void attachTiledMaps(@NotNull final MapModel< G, A, R > mapModel, @NotNull final String @NotNull[] tilePaths, @NotNull final File mapsDirectory, final boolean performAction)
Updates tile paths of a map.
Definition: AttachTiledMaps.java:85
net.sf.gridarta.actions.AttachTiledMaps.tileLinks
final TileLink[] tileLinks
The tile links for the attach map algorithm.
Definition: AttachTiledMaps.java:62
net.sf.gridarta.model.mapmodel.MapFile
The location of a map file with a map directory.
Definition: MapFile.java:31
net.sf.gridarta.model.maparchobject.MapArchObject.setTilePath
void setTilePath(@NotNull Direction direction, @NotNull String tilePath)
Sets a tile path.
net.sf.gridarta.actions.MapSizeMismatchException
Exception thrown if the size of a map file is unexpected.
Definition: MapSizeMismatchException.java:30
net.sf.gridarta.actions.AttachTiledMaps.loadMapControl
MapControl< G, A, R > loadMapControl(@NotNull final MapModel< G, A, R > mapModel, @NotNull final String path)
Loads an adjacent MapControl, ignoring any (I/O-)errors.
Definition: AttachTiledMaps.java:266
net.sf.gridarta.model.mapmanager.MapManager.release
void release(@NotNull MapControl< G, A, R > mapControl)
Releases a MapControl instance.
net.sf.gridarta.utils.Size2D.equals
boolean equals(@Nullable final Object obj)
Definition: Size2D.java:76
net.sf.gridarta.actions.CannotSaveMapFileException
Exception thrown if a map file cannot be saved.
Definition: CannotSaveMapFileException.java:30
net.sf.gridarta.model
net.sf.gridarta.model.archetype.Archetype
Reflects an Archetype.
Definition: Archetype.java:41
net.sf.gridarta.actions.AttachTiledMaps.mapManager
final MapManager< G, A, R > mapManager
The mainControl to use.
Definition: AttachTiledMaps.java:56
net.sf.gridarta.model.mapcontrol.MapControl
Currently nothing more than a marker interface for unification.
Definition: MapControl.java:35
net.sf.gridarta.model.mapcontrol.MapControl.getMapModel
MapModel< G, A, R > getMapModel()
Returns the map model.
net.sf.gridarta.model.maparchobject
Definition: AbstractMapArchObject.java:20
net.sf.gridarta.model.tiles
Definition: MapLink.java:20
net.sf.gridarta.model.mapmodel.MapModel.getMapFile
MapFile getMapFile()
Returns the map file.
net.sf.gridarta.utils.Size2D
The class Size2D represents a 2d rectangular area.
Definition: Size2D.java:30
net.sf.gridarta.utils
Definition: ActionBuilderUtils.java:20
net.sf.gridarta.model.direction
Definition: Direction.java:20
net.sf.gridarta.actions.AttachTiledMaps.AttachTiledMaps
AttachTiledMaps(@NotNull final MapManager< G, A, R > mapManager, @NotNull final TileLink @NotNull[] tileLinks)
Creates a new instance.
Definition: AttachTiledMaps.java:69
net.sf.gridarta.model.maparchobject.MapArchObject.endTransaction
void endTransaction()
Ends a transaction.
net.sf.gridarta.actions.CannotLoadMapFileException
Exception thrown if a map file cannot be loaded.
Definition: CannotLoadMapFileException.java:29