Lucene.Net.Support.IO.FileSupport: Fixed several issues with CreateTempFile()

a) Added overload with no directory
b) Changed implementation to use Path.GetInvalidFileNameCharacters() instead of 
Path.GetInvalidPathCharacters() when checking prefix and suffix
c) Create the temp directory if it doesn't already exist d) Use FileStream 
instead of File.WriteAllText() or the correct exception isn't thrown when the 
file exists
d) On Windows, we check the HResult of the exception instead of checking 
File.Exists() to determine whether the IOException that was thrown is because 
the file already exists
e) Corrected documentation


Project: http://git-wip-us.apache.org/repos/asf/lucenenet/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucenenet/commit/5065f10c
Tree: http://git-wip-us.apache.org/repos/asf/lucenenet/tree/5065f10c
Diff: http://git-wip-us.apache.org/repos/asf/lucenenet/diff/5065f10c

Branch: refs/heads/master
Commit: 5065f10c015c31e84eaed434134f7cb13c95bf3a
Parents: 06f349a
Author: Shad Storhaug <[email protected]>
Authored: Sun Sep 24 16:25:18 2017 +0700
Committer: Shad Storhaug <[email protected]>
Committed: Sun Sep 24 16:25:18 2017 +0700

----------------------------------------------------------------------
 src/Lucene.Net/Support/IO/FileSupport.cs | 184 ++++++++++++++++----------
 1 file changed, 115 insertions(+), 69 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucenenet/blob/5065f10c/src/Lucene.Net/Support/IO/FileSupport.cs
----------------------------------------------------------------------
diff --git a/src/Lucene.Net/Support/IO/FileSupport.cs 
b/src/Lucene.Net/Support/IO/FileSupport.cs
index 491ee35..99ac644 100644
--- a/src/Lucene.Net/Support/IO/FileSupport.cs
+++ b/src/Lucene.Net/Support/IO/FileSupport.cs
@@ -1,24 +1,4 @@
-/*
- *
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- *
-*/
-
+using Lucene.Net.Util;
 using System;
 using System.IO;
 using System.Linq;
@@ -26,87 +6,153 @@ using System.Text;
 
 namespace Lucene.Net.Support.IO
 {
+    /*
+        * Licensed to the Apache Software Foundation (ASF) under one or more
+        * contributor license agreements.  See the NOTICE file distributed with
+        * this work for additional information regarding copyright ownership.
+        * The ASF licenses this file to You under the Apache License, Version 
2.0
+        * (the "License"); you may not use this file except in compliance with
+        * the License.  You may obtain a copy of the License at
+        *
+        *     http://www.apache.org/licenses/LICENSE-2.0
+        *
+        * Unless required by applicable law or agreed to in writing, software
+        * distributed under the License is distributed on an "AS IS" BASIS,
+        * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 
implied.
+        * See the License for the specific language governing permissions and
+        * limitations under the License.
+        */
+
     /// <summary>
     /// Represents the methods to support some operations over files.
     /// </summary>
     public static class FileSupport
     {
-        private static readonly object _lock = new object();
+        private static int ERROR_FILE_EXISTS = 0x0050;
 
         /// <summary>
-        /// Creates a new empty file in the specified directory, using the 
given prefix and suffix strings to generate its name. 
+        /// Creates a new empty file in a random subdirectory of <see 
cref="Path.GetTempPath()"/>, using the given prefix and 
+        /// suffix strings to generate its name.
+        /// </summary>
+        /// <remarks>
         /// If this method returns successfully then it is guaranteed that:
         /// <list type="number">
         /// <item><description>The file denoted by the returned abstract 
pathname did not exist before this method was invoked, and</description></item>
         /// <item><description>Neither this method nor any of its variants 
will return the same abstract pathname again in the current invocation of the 
virtual machine.</description></item>
         /// </list>
-        /// This method provides only part of a temporary-file facility.To 
arrange for a file created by this method to be deleted automatically, use the 
deleteOnExit() method.
-        /// The prefix argument must be at least three characters long. It is 
recommended that the prefix be a short, meaningful string such as "hjb" or 
"mail". The suffix argument may be null, in which case the suffix ".tmp" will 
be used.
-        /// To create the new file, the prefix and the suffix may first be 
adjusted to fit the limitations of the underlying platform.If the prefix is too 
long then it will be truncated, but its first three characters will always be 
preserved.If the suffix is too long then it too will be truncated, but if it 
begins with a period character ('.') then the period and the first three 
characters following it will always be preserved.Once these adjustments have 
been made the name of the new file will be generated by concatenating the 
prefix, five or more internally-generated characters, and the suffix.
-        /// If the directory argument is null then the system-dependent 
default temporary-file directory will be used.The default temporary-file 
directory is specified by the system property java.io.tmpdir.On UNIX systems 
the default value of this property is typically "/tmp" or "/var/tmp"; on 
Microsoft Windows systems it is typically "C:\\WINNT\\TEMP". A different value 
may be given to this system property when the Java virtual machine is invoked, 
but programmatic changes to this property are not guaranteed to have any effect 
upon the temporary directory used by this method.
-        /// 
-        /// Ported over from the java.io.File class. Used by the 
Analysis.Hunspell.Directory
-        /// class, but this can probably be removed when that class is 
upgraded to a more recent
-        /// version of lucene, where it uses the lucene Store.Directory class 
to create a temporary
-        /// file.
+        /// This method provides only part of a temporary-file facility. 
However, the file will not be deleted automatically, 
+        /// it must be deleted by the caller.
+        /// <para/>
+        /// The prefix argument must be at least three characters long. It is 
recommended that the prefix be a short, meaningful 
+        /// string such as "hjb" or "mail". 
+        /// <para/>
+        /// The suffix argument may be null, in which case a random suffix 
will be used.
+        /// <para/>
+        /// Both prefix and suffix must be provided with valid characters for 
the underlying system, as specified by 
+        /// <see cref="Path.GetInvalidFileNameChars()"/>.
+        /// <para/>
+        /// If the directory argument is null then the system-dependent 
default temporary-file directory will be used, 
+        /// with a random subdirectory name. The default temporary-file 
directory is specified by the 
+        /// <see cref="Path.GetTempPath()"/> method. On UNIX systems the 
default value of this property is typically 
+        /// "/tmp" or "/var/tmp"; on Microsoft Windows systems it is typically 
"C:\\Users\\[UserName]\\AppData\Local\Temp".
+        /// </remarks>
+        /// <param name="prefix">The prefix string to be used in generating 
the file's name; must be at least three characters long</param>
+        /// <param name="suffix">The suffix string to be used in generating 
the file's name; may be null, in which case a random suffix will be 
generated</param>
+        /// <returns>A <see cref="FileInfo"/> instance representing the temp 
file that was created.</returns>
+        public static FileInfo CreateTempFile(string prefix, string suffix)
+        {
+            return CreateTempFile(prefix, suffix, null);
+        }
+
+        /// <summary>
+        /// Creates a new empty file in the specified directory, using the 
given prefix and suffix strings to generate its name.
         /// </summary>
+        /// <remarks>
+        /// If this method returns successfully then it is guaranteed that:
+        /// <list type="number">
+        /// <item><description>The file denoted by the returned abstract 
pathname did not exist before this method was invoked, and</description></item>
+        /// <item><description>Neither this method nor any of its variants 
will return the same abstract pathname again in the current invocation of the 
virtual machine.</description></item>
+        /// </list>
+        /// This method provides only part of a temporary-file facility. 
However, the file will not be deleted automatically, 
+        /// it must be deleted by the caller.
+        /// <para/>
+        /// The prefix argument must be at least three characters long. It is 
recommended that the prefix be a short, meaningful 
+        /// string such as "hjb" or "mail". 
+        /// <para/>
+        /// The suffix argument may be null, in which case a random suffix 
will be used.
+        /// <para/>
+        /// Both prefix and suffix must be provided with valid characters for 
the underlying system, as specified by 
+        /// <see cref="Path.GetInvalidFileNameChars()"/>.
+        /// <para/>
+        /// If the directory argument is null then the system-dependent 
default temporary-file directory will be used, 
+        /// with a random subdirectory name. The default temporary-file 
directory is specified by the 
+        /// <see cref="Path.GetTempPath()"/> method. On UNIX systems the 
default value of this property is typically 
+        /// "/tmp" or "/var/tmp"; on Microsoft Windows systems it is typically 
"C:\\Users\\[UserName]\\AppData\Local\Temp".
+        /// </remarks>
         /// <param name="prefix">The prefix string to be used in generating 
the file's name; must be at least three characters long</param>
         /// <param name="suffix">The suffix string to be used in generating 
the file's name; may be null, in which case a random suffix will be 
generated</param>
         /// <param name="directory">The directory in which the file is to be 
created, or null if the default temporary-file directory is to be used</param>
-        /// <returns></returns>
+        /// <returns>A <see cref="FileInfo"/> instance representing the temp 
file that was created.</returns>
         public static FileInfo CreateTempFile(string prefix, string suffix, 
DirectoryInfo directory)
         {
-            lock (_lock)
-            {
-                if (string.IsNullOrEmpty(prefix))
-                    throw new ArgumentNullException("prefix");
-                if (prefix.Length < 3)
-                    throw new ArgumentException("Prefix string too short");
+            if (string.IsNullOrEmpty(prefix))
+                throw new ArgumentNullException("prefix");
+            if (prefix.Length < 3)
+                throw new ArgumentException("Prefix string too short");
 
-                // Ensure the strings passed don't contain invalid characters
-                char[] invalid = Path.GetInvalidPathChars();
+            // Ensure the strings passed don't contain invalid characters
+            char[] invalid = Path.GetInvalidFileNameChars();
 
-                if (prefix.ToCharArray().Intersect(invalid).Any())
-                    throw new ArgumentException(string.Format("Prefix contains 
invalid characters. You may not use any of '{0}'", string.Join(", ", invalid)));
-                if (suffix != null && 
suffix.ToCharArray().Intersect(invalid).Any())
-                    throw new ArgumentException(string.Format("Suffix contains 
invalid characters. You may not use any of '{0}'", string.Join(", ", invalid)));
+            if (prefix.ToCharArray().Intersect(invalid).Any())
+                throw new ArgumentException(string.Format("Prefix contains 
invalid characters. You may not use any of '{0}'", string.Join(", ", invalid)));
+            if (suffix != null && 
suffix.ToCharArray().Intersect(invalid).Any())
+                throw new ArgumentException(string.Format("Suffix contains 
invalid characters. You may not use any of '{0}'", string.Join(", ", invalid)));
 
-                // If no directory supplied, create one.
-                if (directory == null)
+            // If no directory supplied, create one.
+            if (directory == null)
+            {
+                directory = new DirectoryInfo(Path.Combine(Path.GetTempPath(), 
Path.GetFileNameWithoutExtension(Path.GetRandomFileName())));
+            }
+            // Ensure the directory exists (this does nothing if it already 
exists, although may throw exceptions in cases where permissions are changed)
+            directory.Create();
+            string fileName = string.Empty;
+
+            while (true)
+            {
+                fileName = NewTempFileName(prefix, suffix, directory);
+
+                if (File.Exists(fileName))
                 {
-                    directory = new DirectoryInfo(Path.GetTempPath());
+                    continue;
                 }
-                string fileName = string.Empty;
 
-                while (true)
+                try
                 {
-                    fileName = NewTempFileName(prefix, suffix, directory);
-
-                    if (File.Exists(fileName))
+                    // Create the file, and close it immediately
+                    using (var stream = new FileStream(fileName, 
FileMode.CreateNew, FileAccess.Write, FileShare.Read))
                     {
-                        continue;
+                        break;
                     }
-
-                    try
+                }
+                catch (IOException e)
+                {
+                    // If the error was because the file exists, try again.
+                    // On Windows, we can rely on the constant, but we need to 
fallback
+                    // to doing a physical file check to be portable across 
platforms.
+                    if (Constants.WINDOWS && (e.HResult & 0xFFFF) == 
ERROR_FILE_EXISTS)
                     {
-                        // Create the file, and close it immediately
-                        File.WriteAllText(fileName, string.Empty, new 
UTF8Encoding(encoderShouldEmitUTF8Identifier: false) /* No BOM */);
-                        break;
+                        continue;
                     }
-                    catch (IOException e)
+                    else if (!Constants.WINDOWS && File.Exists(fileName))
                     {
-                        // If the error was because the file exists, try again
-                        if (File.Exists(fileName))
-                        {
-                            continue;
-                        }
-
-                        // else rethrow it
-                        throw e;
+                        continue;
                     }
+
+                    // else rethrow it
+                    throw;
                 }
-                return new FileInfo(fileName);
             }
+            return new FileInfo(fileName);
         }
 
         /// <summary>

Reply via email to