001/* 002 * Gridarta MMORPG map editor for Crossfire, Daimonin and similar games. 003 * Copyright (C) 2000-2010 The Gridarta Developers. 004 * 005 * This program is free software; you can redistribute it and/or modify 006 * it under the terms of the GNU General Public License as published by 007 * the Free Software Foundation; either version 2 of the License, or 008 * (at your option) any later version. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 013 * GNU General Public License for more details. 014 * 015 * You should have received a copy of the GNU General Public License along 016 * with this program; if not, write to the Free Software Foundation, Inc., 017 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 018 */ 019 020package net.sf.gridarta.model.mappathnormalizer; 021 022import java.io.File; 023import java.io.IOException; 024import net.sf.gridarta.model.mapmodel.MapModel; 025import net.sf.gridarta.model.settings.ProjectSettings; 026import org.jetbrains.annotations.NotNull; 027import org.jetbrains.annotations.Nullable; 028 029/** 030 * Normalizes map path specifications into {@link File Files}. 031 * @author Andreas Kirschbaum 032 */ 033public class MapPathNormalizer { 034 035 /** 036 * The project settings instance. 037 */ 038 @NotNull 039 private final ProjectSettings projectSettings; 040 041 /** 042 * Creates a new instance. 043 * @param projectSettings the project settings instance 044 */ 045 public MapPathNormalizer(@NotNull final ProjectSettings projectSettings) { 046 this.projectSettings = projectSettings; 047 } 048 049 /** 050 * Normalizes a map path relative to a {@link MapModel}. 051 * @param mapModel the map model to start from 052 * @param path the destination path 053 * @return the normalized destination file 054 * @throws InvalidPathException if the destination file is invalid 055 * @throws IOErrorException if an I/O error occurs 056 * @throws RelativePathOnUnsavedMapException if the path is relative and the 057 * map has not yet been saved 058 * @throws SameMapException if the destination path points to the source 059 * map 060 */ 061 @NotNull 062 public File normalizeMapPath(@NotNull final MapModel<?, ?, ?> mapModel, @NotNull final String path) throws InvalidPathException, IOErrorException, RelativePathOnUnsavedMapException, SameMapException { 063 return normalizeMapPath(mapModel.getMapFile(), path); 064 } 065 066 /** 067 * Normalizes a map path relative to a {@link File}. 068 * @param mapFile the file to start from 069 * @param path the destination path 070 * @return the normalized destination file 071 * @throws InvalidPathException if the destination file is invalid 072 * @throws IOErrorException if an I/O error occurs 073 * @throws RelativePathOnUnsavedMapException if the path is relative and the 074 * map has not yet been saved 075 * @throws SameMapException if the destination path points to the source 076 * map 077 */ 078 @NotNull 079 private File normalizeMapPath(@Nullable final File mapFile, @NotNull final String path) throws InvalidPathException, IOErrorException, RelativePathOnUnsavedMapException, SameMapException { 080 @NotNull final File newFile; 081 if (path.startsWith(File.pathSeparator) || path.startsWith("/")) { 082 // we have an absolute path: 083 newFile = new File(projectSettings.getMapsDirectory().getAbsolutePath(), path.substring(1)); 084 } else { 085 // we have a relative path: 086 if (mapFile == null) { 087 throw new RelativePathOnUnsavedMapException(path); 088 } 089 newFile = new File(mapFile.getParent(), path); 090 } 091 if (path.isEmpty() || (mapFile != null && newFile.equals(mapFile))) { 092 throw new SameMapException(); 093 } 094 095 if (!newFile.exists() || newFile.isDirectory()) { 096 // The path is wrong 097 // TODO: Differ between non-existing paths, wrongly formatted paths and directories - give more info to the user. 098 throw new InvalidPathException(newFile); 099 } 100 101 // its important to force the canonical file here or the 102 // file path is added every time we use a ../ or a ./ . 103 // This results in giant file names like "xx/../yy/../xx/../yy/.." 104 // and after some times in buffer overflows. 105 final File canonicalNewFile; 106 try { 107 canonicalNewFile = newFile.getCanonicalFile(); 108 } catch (final IOException ex) { 109 throw new IOErrorException(newFile, ex); 110 } 111 112 return canonicalNewFile; 113 } 114 115}