1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *      http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 */
18package org.apache.tools.ant.util;
19
20import org.apache.tools.ant.BuildException;
21import org.apache.tools.ant.taskdefs.condition.Os;
22
23import java.io.File;
24import java.util.Random;
25
26/**
27 * This class also encapsulates methods which allow Files to be
28 * referred to using abstract path names which are translated to native
29 * system file paths at runtime as well as copying files or setting
30 * their last modification time.
31 *
32 */
33public class FileUtils {
34    private static final int DELETE_RETRY_SLEEP_MILLIS = 10;
35    private static final int EXPAND_SPACE = 50;
36    private static final FileUtils PRIMARY_INSTANCE = new FileUtils();
37
38    //get some non-crypto-grade randomness from various places.
39    private static Random rand = new Random(System.currentTimeMillis()
40            + Runtime.getRuntime().freeMemory());
41
42    private static final boolean ON_NETWARE = Os.isFamily("netware");
43    private static final boolean ON_DOS = Os.isFamily("dos");
44    private static final boolean ON_WIN9X = Os.isFamily("win9x");
45    private static final boolean ON_WINDOWS = Os.isFamily("windows");
46
47    static final int BUF_SIZE = 8192;
48
49
50    /**
51     * The granularity of timestamps under FAT.
52     */
53    public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000;
54
55    /**
56     * The granularity of timestamps under Unix.
57     */
58    public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000;
59
60    /**
61     * The granularity of timestamps under the NT File System.
62     * NTFS has a granularity of 100 nanoseconds, which is less
63     * than 1 millisecond, so we round this up to 1 millisecond.
64     */
65    public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1;
66
67    /**
68     * A one item cache for fromUri.
69     * fromUri is called for each element when parseing ant build
70     * files. It is a costly operation. This just caches the result
71     * of the last call.
72     */
73    private Object cacheFromUriLock = new Object();
74    private String cacheFromUriRequest = null;
75    private String cacheFromUriResponse = null;
76
77    /**
78     * Factory method.
79     *
80     * @return a new instance of FileUtils.
81     * @deprecated since 1.7.
82     *             Use getFileUtils instead,
83     * FileUtils do not have state.
84     */
85    @Deprecated
86    public static FileUtils newFileUtils() {
87        return new FileUtils();
88    }
89
90    /**
91     * Method to retrieve The FileUtils, which is shared by all users of this
92     * method.
93     * @return an instance of FileUtils.
94     * @since Ant 1.6.3
95     */
96    public static FileUtils getFileUtils() {
97        return PRIMARY_INSTANCE;
98    }
99
100    /**
101     * Empty constructor.
102     */
103    protected FileUtils() {
104    }
105
106    /**
107     * Verifies that the specified filename represents an absolute path.
108     * Differs from new java.io.File("filename").isAbsolute() in that a path
109     * beginning with a double file separator--signifying a Windows UNC--must
110     * at minimum match "\\a\b" to be considered an absolute path.
111     * @param filename the filename to be checked.
112     * @return true if the filename represents an absolute path.
113     * @throws java.lang.NullPointerException if filename is null.
114     * @since Ant 1.6.3
115     */
116    public static boolean isAbsolutePath(String filename) {
117        int len = filename.length();
118        if (len == 0) {
119            return false;
120        }
121        char sep = File.separatorChar;
122        filename = filename.replace('/', sep).replace('\\', sep);
123        char c = filename.charAt(0);
124        if (!(ON_DOS || ON_NETWARE)) {
125            return (c == sep);
126        }
127        if (c == sep) {
128            // CheckStyle:MagicNumber OFF
129            if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) {
130                return false;
131            }
132            // CheckStyle:MagicNumber ON
133            int nextsep = filename.indexOf(sep, 2);
134            return nextsep > 2 && nextsep + 1 < len;
135        }
136        int colon = filename.indexOf(':');
137        return (Character.isLetter(c) && colon == 1
138                && filename.length() > 2 && filename.charAt(2) == sep)
139                || (ON_NETWARE && colon > 0);
140    }
141
142    /**
143     * Dissect the specified absolute path.
144     * @param path the path to dissect.
145     * @return String[] {root, remaining path}.
146     * @throws java.lang.NullPointerException if path is null.
147     * @since Ant 1.7
148     */
149    public String[] dissect(String path) {
150        char sep = File.separatorChar;
151        path = path.replace('/', sep).replace('\\', sep);
152
153        // make sure we are dealing with an absolute path
154        if (!isAbsolutePath(path)) {
155            throw new BuildException(path + " is not an absolute path");
156        }
157        String root = null;
158        int colon = path.indexOf(':');
159        if (colon > 0 && (ON_DOS || ON_NETWARE)) {
160
161            int next = colon + 1;
162            root = path.substring(0, next);
163            char[] ca = path.toCharArray();
164            root += sep;
165            //remove the initial separator; the root has it.
166            next = (ca[next] == sep) ? next + 1 : next;
167
168            StringBuffer sbPath = new StringBuffer();
169            // Eliminate consecutive slashes after the drive spec:
170            for (int i = next; i < ca.length; i++) {
171                if (ca[i] != sep || ca[i - 1] != sep) {
172                    sbPath.append(ca[i]);
173                }
174            }
175            path = sbPath.toString();
176        } else if (path.length() > 1 && path.charAt(1) == sep) {
177            // UNC drive
178            int nextsep = path.indexOf(sep, 2);
179            nextsep = path.indexOf(sep, nextsep + 1);
180            root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path;
181            path = path.substring(root.length());
182        } else {
183            root = File.separator;
184            path = path.substring(1);
185        }
186        return new String[] {root, path};
187    }
188
189}
190