View Javadoc

1   /**
2    * This file Copyright (c) 2003-2014 Magnolia International
3    * Ltd.  (http://www.magnolia-cms.com). All rights reserved.
4    *
5    *
6    * This file is dual-licensed under both the Magnolia
7    * Network Agreement and the GNU General Public License.
8    * You may elect to use one or the other of these licenses.
9    *
10   * This file is distributed in the hope that it will be
11   * useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
12   * implied warranty of MERCHANTABILITY or FITNESS FOR A
13   * PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
14   * Redistribution, except as permitted by whichever of the GPL
15   * or MNA you select, is prohibited.
16   *
17   * 1. For the GPL license (GPL), you can redistribute and/or
18   * modify this file under the terms of the GNU General
19   * Public License, Version 3, as published by the Free Software
20   * Foundation.  You should have received a copy of the GNU
21   * General Public License, Version 3 along with this program;
22   * if not, write to the Free Software Foundation, Inc., 51
23   * Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
24   *
25   * 2. For the Magnolia Network Agreement (MNA), this file
26   * and the accompanying materials are made available under the
27   * terms of the MNA which accompanies this distribution, and
28   * is available at http://www.magnolia-cms.com/mna.html
29   *
30   * Any modifications to this file must keep this entire header
31   * intact.
32   *
33   */
34  package info.magnolia.cms.core;
35  
36  import java.io.File;
37  
38  import javax.jcr.RepositoryException;
39  import javax.jcr.Session;
40  
41  import org.apache.commons.lang.StringUtils;
42  import org.safehaus.uuid.UUIDGenerator;
43  
44  
45  /**
46   * Utility class to retrieve files or directory used by Magnolia. Examples: cache directory, tmp files, ..
47   * @version 2.0 $Id$
48   */
49  public final class Path {
50      /**
51       * New unlabeled nodes default name.
52       */
53      private static final String DEFAULT_UNTITLED_NODE_NAME = "untitled";
54  
55      public static final String SELECTOR_DELIMITER = "~";
56  
57      /**
58       * Utility class, don't instantiate.
59       */
60      private Path() {
61          // unused
62      }
63  
64      /**
65       * Gets the cache directory path (cms.cache.startdir) as set with Java options while startup or in web.xml.
66       * @return Cache directory path
67       */
68      public static String getCacheDirectoryPath() {
69          return getCacheDirectory().getAbsolutePath();
70      }
71  
72      public static File getCacheDirectory() {
73          String path = SystemProperty.getProperty(SystemProperty.MAGNOLIA_CACHE_STARTDIR);
74          File dir = isAbsolute(path) ? new File(path) : new File(getAppRootDir(), path);
75          dir.mkdirs();
76          return dir;
77      }
78  
79      /**
80       * Gets the temporary directory path (cms.upload.tmpdir) as set with Java options while startup or in web.xml.
81       * @return Temporary directory path
82       */
83      public static String getTempDirectoryPath() {
84          return getTempDirectory().getAbsolutePath();
85      }
86  
87      public static File getTempDirectory() {
88          String path = SystemProperty.getProperty(SystemProperty.MAGNOLIA_UPLOAD_TMPDIR);
89          File dir = isAbsolute(path) ? new File(path) : new File(getAppRootDir(), path);
90          dir.mkdirs();
91          return dir;
92      }
93  
94      /**
95       * Gets cms.exchange.history file location as set with Java options while startup or in web.xml.
96       * @return exchange history file location
97       */
98      public static String getHistoryFilePath() {
99          return getHistoryFile().getAbsolutePath();
100     }
101 
102     public static File getHistoryFile() {
103         String path = SystemProperty.getProperty(SystemProperty.MAGNOLIA_EXCHANGE_HISTORY);
104         return isAbsolute(path) ? new File(path) : new File(getAppRootDir(), path);
105     }
106 
107     /**
108      * Gets repositories file location as set with Java options while startup or in web.xml.
109      * @return file location
110      */
111     public static String getRepositoriesConfigFilePath() {
112         return getRepositoriesConfigFile().getAbsolutePath();
113     }
114 
115     public static File getRepositoriesConfigFile() {
116         String path = SystemProperty.getProperty(SystemProperty.MAGNOLIA_REPOSITORIES_CONFIG);
117         return isAbsolute(path) ? new File(path) : new File(getAppRootDir(), path);
118     }
119 
120     /**
121      * Gets the root directory for the magnolia web application.
122      * @return magnolia root dir
123      */
124     public static File getAppRootDir() {
125         return new File(SystemProperty.getProperty(SystemProperty.MAGNOLIA_APP_ROOTDIR));
126     }
127 
128     /**
129      * Gets absolute filesystem path, adds application root if path is not absolute.
130      */
131     public static String getAbsoluteFileSystemPath(String path) {
132         if (isAbsolute(path)) {
133             return path;
134         }
135         // using the file() constructor will allow relative paths in the form ../../apps
136         return new File(getAppRootDir(), path).getAbsolutePath();
137     }
138 
139     public static String getUniqueLabel(HierarchyManager hierarchyManager, String parent, String label) {
140         if (parent.equals("/")) {
141             parent = StringUtils.EMPTY;
142         }
143         while (hierarchyManager.isExist(parent + "/" + label)) {
144             label = createUniqueName(label);
145         }
146         return label;
147     }
148 
149     public static String getUniqueLabel(Session session, String parent, String label) throws RepositoryException {
150         if (parent.equals("/")) {
151             parent = StringUtils.EMPTY;
152         }
153         while (session.itemExists(parent + "/" + label)) {
154             label = createUniqueName(label);
155         }
156         return label;
157     }
158 
159     public static String getUniqueLabel(Content parent, String label) {
160         try {
161             while (parent.hasContent(label) || parent.hasNodeData(label)) {
162                 label = createUniqueName(label);
163             }
164         }
165         catch (RepositoryException e) {
166             label = UUIDGenerator.getInstance().generateRandomBasedUUID().toString();
167         }
168         return label;
169     }
170 
171     public static boolean isAbsolute(String path) {
172 
173         if (path == null) {
174             return false;
175         }
176 
177         if (path.startsWith("/") || path.startsWith(File.separator)) {
178             return true;
179         }
180 
181         // windows c:
182         if (path.length() >= 3 && Character.isLetter(path.charAt(0)) && path.charAt(1) == ':') {
183             return true;
184         }
185 
186         return false;
187     }
188 
189     /**
190      * Replace illegal characters based on system property magnolia.ut8.enabled.
191      * @param label label to validate
192      * @return validated label
193      */
194     public static String getValidatedLabel(String label)
195     {
196         String charset = StringUtils.EMPTY;
197         if ((SystemProperty.getBooleanProperty(SystemProperty.MAGNOLIA_UTF8_ENABLED)))
198         {
199             charset = "UTF-8";
200         }
201         return getValidatedLabel(label, charset);
202 
203     }
204 
205     /**
206      * If charset equals <code>UTF-8</code>, replaces the following characters with a dash <code>-</code> :
207      * <p>
208      * Jackrabbit not allowed {@code 32: [ ] 91: [[] 93: []] 42: [*] 34: ["] 58 [:] 92: [\] 39 :[']}
209      * <p>
210      * URL not valid {@code 59: [;] 47: [/] 63: [?] 43: [+] 37: [%] 33: [!] 35:[#] 94: [^]}.
211      * <p>
212      * Otherwise, replaces illegal characters with a dash <code>-</code> except for {@code [_] [0-9], [A-Z], [a-z], [-], [_], [.]}.
213      * <p>
214      * Please notice that a valid label can not begin with dot or period <code>[.]</code>.
215      *
216      * @return a validated label for a node.
217      */
218     public static String getValidatedLabel(String label, String charset)
219     {
220         if(StringUtils.isEmpty(label)) {
221             return DEFAULT_UNTITLED_NODE_NAME;
222         }
223         final StringBuilder newLabel = new StringBuilder(label.length());
224 
225         //label cannot begin with . (dot)
226         int ch = label.charAt(0);
227         if(!isCharValid(ch, charset) || ch == 46) {
228             newLabel.append("-");
229         } else {
230             newLabel.append(label.charAt(0));
231         }
232 
233         for (int i = 1; i < label.length(); i++)
234         {
235             int charCode = label.charAt(i);
236             if (isCharValid(charCode, charset))
237             {
238                 newLabel.append(label.charAt(i));
239             }
240             else
241             {
242                 newLabel.append("-");
243             }
244         }
245         if (newLabel.length() == 0)
246         {
247             newLabel.append(DEFAULT_UNTITLED_NODE_NAME);
248         }
249         return newLabel.toString();
250     }
251 
252     /**
253      * @param charCode char code
254      * @param charset charset (ex. UTF-8)
255      * @return true if char can be used as a content name
256      */
257     public static boolean isCharValid(int charCode, String charset)
258     {
259         //TODO fgrilli: we now allow dots (.) in JR local names but actually in JR 2.0 other chars could be allowed as well
260         //(see http://www.day.com/specs/jcr/2.0/3_Repository_Model.html paragraph 2.2 and org.apache.jackrabbit.util.XMLChar.isValid()).
261         //Also, now that we're on java 6 and JR 2.0 should the check for the charset be dropped?
262 
263         // http://www.ietf.org/rfc/rfc1738.txt
264         // safe = "$" | "-" | "_" | "." | "+"
265         // extra = "!" | "*" | "'" | "(" | ")" | ","
266         // national = "{" | "}" | "|" | "\" | "^" | "~" | "[" | "]" | "`"
267         // punctuation = "<" | ">" | "#" | "%" | <">
268         // reserved = ";" | "/" | "?" | ":" | "@" | "&" | "="
269 
270         if ("UTF-8".equals(charset))
271         {
272             // jackrabbit not allowed 32: [ ] 91: [[] 93: []] 42: [*] 34: ["] 46: [.] 58 [:] 92: [\] 39 :[']
273             // url not valid 59: [;] 47: [/] 63: [?] 43: [+] 37: [%] 33: [!] 35:[#]
274             if (charCode != 32
275                 && charCode != '['
276                 && charCode != ']'
277                 && charCode != '*'
278                 && charCode != '"'
279                 && charCode != ':'
280                 && charCode != 92
281                 && charCode != 39
282                 && charCode != ';'
283                 && charCode != '/'
284                 && charCode != '?'
285                 && charCode != '+'
286                 && charCode != '%'
287                 && charCode != '!'
288                 && charCode != '#'
289                 && charCode != '@'
290                 && charCode != '&'
291                 && charCode != '=')
292             {
293                 return true;
294             }
295         }
296         else
297         {
298             // charCodes: 48-57: [0-9]; 65-90: [A-Z]; 97-122: [a-z]; 45: [-]; 95:[_]
299             if (((charCode >= 48) && (charCode <= 57))
300                     || ((charCode >= 65) && (charCode <= 90))
301                     || ((charCode >= 97) && (charCode <= 122))
302                     || charCode == 45
303                     || charCode == 46
304                     || charCode == 95)
305             {
306                 return true;
307             }
308 
309         }
310         return false;
311 
312     }
313 
314     private static String createUniqueName(String baseName) {
315         int pos;
316         for (pos = baseName.length() - 1; pos >= 0; pos--) {
317             char c = baseName.charAt(pos);
318             if (c < '0' || c > '9') {
319                 break;
320             }
321         }
322         String base;
323         int cnt;
324         if (pos == -1) {
325             if (baseName.length() > 1) {
326                 pos = baseName.length() - 2;
327             }
328         }
329         if (pos == -1) {
330             base = baseName;
331             cnt = -1;
332         }
333         else {
334             pos++;
335             base = baseName.substring(0, pos);
336             if (pos == baseName.length()) {
337                 cnt = -1;
338             }
339             else {
340                 cnt = new Integer(baseName.substring(pos)).intValue();
341             }
342         }
343         return (base + ++cnt);
344     }
345 
346     public static String getAbsolutePath(String path, String label) {
347         if (StringUtils.isEmpty(path) || (path.equals("/"))) {
348             return "/" + label;
349         }
350 
351         return path + "/" + label;
352     }
353 
354     public static String getAbsolutePath(String path) {
355         if (!path.startsWith("/")) {
356             return "/" + path;
357         }
358         return path;
359     }
360 
361     /**
362      * @deprecated since 4.0 - untested and unused
363      */
364     @Deprecated
365     public static String getNodePath(String path, String label) {
366         if (StringUtils.isEmpty(path) || (path.equals("/"))) {
367             return label;
368         }
369         return getNodePath(path + "/" + label);
370     }
371 
372     /**
373      * @deprecated since 4.0 - untested and unused
374      */
375     @Deprecated
376     public static String getNodePath(String path) {
377         if (path.startsWith("/")) {
378             return path.replaceFirst("/", StringUtils.EMPTY);
379         }
380         return path;
381     }
382 
383     /**
384      * @deprecated since 4.0 - untested and unused
385      */
386     @Deprecated
387     public static String getParentPath(String path) {
388         int lastIndexOfSlash = path.lastIndexOf("/");
389         if (lastIndexOfSlash > 0) {
390             return StringUtils.substringBefore(path, "/");
391         }
392         return "/";
393     }
394 }