001/*
002        $Id: Path.java 3207 2009-04-09 06:48:11Z gregory $
003
004        Copyright (C) 2006 Gregory Vincic, Olle Mansson
005        Copyright (C) 2007 Gregory Vincic
006
007        This file is part of Proteios.
008        Available at http://www.proteios.org/
009
010        Proteios is free software; you can redistribute it and/or modify it
011        under the terms of the GNU General Public License as published by
012        the Free Software Foundation; either version 2 of the License, or
013        (at your option) any later version.
014
015        Proteios is distributed in the hope that it will be useful, but
016        WITHOUT ANY WARRANTY; without even the implied warranty of
017        MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
018        General Public License for more details.
019
020        You should have received a copy of the GNU General Public License
021        along with this program; if not, write to the Free Software
022        Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
023        02111-1307, USA.
024*/
025package org.proteios.core;
026
027import java.util.regex.Pattern;
028
029/**
030        This class is used represent the path to a {@link Directory Directory}
031        or {@link File File} item. Some examples:
032
033        <ul>
034        <li>/filename
035        <li>/directory/filename
036        <li>/directory/subdirectory/filename
037        <li>~userlogin/directory/filename
038        <li>/directory
039        <li>/directory/subdirectory
040        <li>~userlogin
041        <li>~userlogin/directory
042        </ul>
043
044        @author enell
045        @version 2.0
046        @base.modified $Date: 2009-04-09 08:48:11 +0200 (Thu, 09 Apr 2009) $
047
048*/
049public class Path
050{
051        /**
052                These characters are not valid within a file or directory name:
053                {@value}
054        */
055        public static final String INVALID_CHARACTERS = "~\\/:;*?<>|";
056
057        /**
058                A regexp checking for invalid characters.
059        */
060        private static final Pattern invalid = Pattern.compile("[\\~\\\\\\/\\:\\;\\*\\?\\<\\>\\|]");
061
062        /**
063                Check if a name is valid as a partial path name, ie. it must not contain any
064                of the characters in {@link #INVALID_CHARACTERS}.
065                
066                @param name The string to be tested.
067                @return TRUE if this name is valid.
068        */
069        public static boolean isValidName(String name)
070        {
071                return !invalid.matcher(name).find();
072        }
073
074        /**
075                The type of the path.
076        */
077        private Path.Type type;
078
079        /**
080                The userlogin part of the path.
081        */
082        private String userlogin;
083
084        /**
085                The directory part of the path.
086        */
087        private String[] directories;
088
089        /**
090                The filename part of the path.
091        */
092        private String filename;
093
094        /**
095                Create a new <code>Path</code> object by parsing the given string.
096                @param path The string representation of a path
097                @param type If the path represents a {@link Type#FILE} or
098                        {@link Type#DIRECTORY}
099                @throws InvalidPathException If the path is not valid
100        */
101        public Path(String path, Path.Type type)
102                throws InvalidPathException
103        {
104                this.type = type;
105
106                if (!path.startsWith("/") && !path.startsWith("~"))
107                {
108                        throw new InvalidPathException("Path must begin with a / or ~: "+path);
109                }
110                String[] parts = path.split("/");
111                if (path.startsWith("~"))
112                {
113                        userlogin = parts[0].substring(1);
114                        if (userlogin == null || "".equals(userlogin))
115                        {
116                                throw new InvalidPathException("Path must contain a user login: "+path);
117                        }
118                        parts[0] = "";
119                }
120                if (type == Type.FILE)
121                {
122                        if (parts.length < 2)
123                        {
124                                throw new InvalidPathException("Path must contain a filename: "+path);
125                        }
126                        filename = parts[parts.length-1];
127                        parts[parts.length-1] = "";
128                }
129                
130                int i = 0;
131                for (String part : parts)
132                {
133                        if (!"".equals(part))
134                        {
135                                if (!Path.isValidName(part)) 
136                                {
137                                        throw new InvalidPathException("Path contains invalid characters ("+Path.INVALID_CHARACTERS+"): "+path);
138                                }
139                                parts[i] = part;
140                                i++;
141                        }
142                }
143                directories = new String[i];
144                System.arraycopy(parts, 0, directories, 0, i);
145        }
146
147        /**
148                Create a new <code>Path</code> object.
149                @param userlogin The userlogin part of the path
150                @param directories The directories in the path
151                @param filename The filename part of the path
152        */
153        Path(String userlogin, String[] directories, String filename)
154        {
155                this.userlogin = userlogin;
156                this.directories = directories;
157                this.filename = filename;
158                this.type = (filename == null) ? Path.Type.DIRECTORY : Path.Type.FILE;
159        }
160
161        /**
162                Create a new <code>Path</code> object.
163                
164                @param directoryPath The directory path
165                @param filename The filename part of the path
166        */
167        Path(Path directoryPath, String filename)
168        {
169                this(directoryPath.userlogin, directoryPath.directories, filename);
170        }
171
172        /**
173                Get the type of the path.
174                @return The {@link Path.Type} of the Path
175        */
176        public Path.Type getType()
177        {
178                return type;
179        }
180
181        /**
182                Get the userlogin part of the path.
183                @return The login of the user
184        */
185        public String getUserLogin()
186        {
187                return userlogin;
188        }
189
190        /**
191                Get the number of directories on the path.
192                @return The number of directories
193        */
194        public int getDirectoryCount()
195        {
196                return directories == null ? 0 : directories.length;
197        }
198
199        /**
200                Get the name of the i:th directory on the path, starting with 0.
201                @param i Should be between 0 and {@link #getDirectoryCount() getDirectoryCount-1}
202                @return The name of the directory
203        */
204        public String getDirectory(int i)
205        {
206                return directories[i];
207        }
208
209        /**
210                Get the filename part of the path.
211                @return The name of the file
212        */
213        public String getFilename()
214        {
215                return filename;
216        }
217
218        /**
219                Get the string representation of the path.
220                
221                @return The string representation of this path.
222        */
223        @Override
224        public String toString()
225        {
226                StringBuilder sb = new StringBuilder();
227                if (userlogin != null)
228                {
229                        sb.append("~").append(userlogin);
230                }
231                for (String directory : directories)
232                {
233                        sb.append("/").append(directory);
234                }
235                if (filename != null && getType() == Type.FILE)
236                {
237                        sb.append("/").append(filename);
238                }
239                if (sb.length() == 0) sb.append("/"); // For the root directory
240                return sb.toString();
241        }
242
243        /**
244                An enum that describes what type the path is
245         
246                @author enell
247                @version 2.0
248        */
249        public enum Type
250        {
251                /**
252                        The path is a path to a {@link File}.
253                */
254                FILE, 
255                
256                /**
257                        The path is a path to a {@link Directory}.
258                */
259                DIRECTORY;
260        }
261}