ClassFileNameHandler.java revision bbf4dbba6127ef96e316060b2b4ec292627a4078
1f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com/*
2f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * [The "BSD licence"]
3f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * Copyright (c) 2010 Ben Gruver
4f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * All rights reserved.
5f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com *
6f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * Redistribution and use in source and binary forms, with or without
7f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * modification, are permitted provided that the following conditions
8f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * are met:
9f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * 1. Redistributions of source code must retain the above copyright
10f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com *    notice, this list of conditions and the following disclaimer.
11f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * 2. Redistributions in binary form must reproduce the above copyright
12f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com *    notice, this list of conditions and the following disclaimer in the
13f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com *    documentation and/or other materials provided with the distribution.
14f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * 3. The name of the author may not be used to endorse or promote products
15f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com *    derived from this software without specific prior written permission.
16f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com *
17f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com */
28f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
29f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.compackage org.jf.util;
30f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
31f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.comimport ds.tree.RadixTree;
32f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.comimport ds.tree.RadixTreeImpl;
33f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
34f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.comimport java.io.*;
35f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.comimport java.nio.CharBuffer;
36f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
37f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com/**
38f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * This class checks for case-insensitive file systems, and generates file names based on a given class name, that are
39f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * guaranteed to be unique. When "colliding" class names are found, it appends a numeric identifier to the end of the
40f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com * class name to distinguish it from another class with a name that differes only by case. i.e. a.smali and a_2.smali
41f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com */
42f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.compublic class ClassFileNameHandler {
43f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    private PackageNameEntry top;
44f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    private String fileExtension;
45f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
46f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    public ClassFileNameHandler(File path, String fileExtension) {
47f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        this.top = new PackageNameEntry(path);
48f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        this.fileExtension = fileExtension;
49f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    }
50f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
51f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    public File getUniqueFilenameForClass(String className) {
52f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //class names should be passed in the normal dalvik style, with a leading L, a trailing ;, and using
53f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //'/' as a separator.
54f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        if (className.charAt(0) != 'L' || className.charAt(className.length()-1) != ';') {
55f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            throw new RuntimeException("Not a valid dalvik class name");
56f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        }
57f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
58f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        String blah;
59f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        int packageElementCount = 1;
60f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        for (int i=1; i<className.length()-1; i++) {
61f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            if (className.charAt(i) == '/') {
62f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                packageElementCount++;
63f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            }
64f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        }
65f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
66f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        String[] packageElements = new String[packageElementCount];
67f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        int elementIndex = 0;
68f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        int elementStart = 1;
69f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        for (int i=1; i<className.length()-1; i++) {
70f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            if (className.charAt(i) == '/') {
71f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                //if the first char after the initial L is a '/', or if there are
72f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                //two consecutive '/'
73f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                if (i-elementStart==0) {
74f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                    throw new RuntimeException("Not a valid dalvik class name");
75f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                }
76f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
77f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                packageElements[elementIndex++] = className.substring(elementStart, i);
78f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                elementStart = ++i;
79f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            }
80f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        }
81f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
82f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //at this point, we have added all the package elements to packageElements, but still need to add
83f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //the final class name. elementStart should point to the beginning of the class name
84f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
85f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //this will be true if the class ends in a '/', i.e. Lsome/package/className/;
86f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        if (elementStart >= className.length()-1) {
87f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            throw new RuntimeException("Not a valid dalvik class name");
88f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        }
89f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        packageElements[elementIndex] = className.substring(elementStart, className.length()-1);
90f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
91f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        return top.addUniqueChild(packageElements, 0);
92f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    }
93f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
94f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    private abstract class FileSystemEntry {
95f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        public final File file;
96f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
97f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        public FileSystemEntry(File file) {
98f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            this.file = file;
99f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        }
100f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
101f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        public abstract File addUniqueChild(String[] pathElements, int pathElementsIndex);
102f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
103f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        public FileSystemEntry makeVirtual(File parent) {
104f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            return new VirtualGroupEntry(this, parent);
105f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        }
106f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    }
107f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
108f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    private class PackageNameEntry extends FileSystemEntry {
109f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //this contains the FileSystemEntries for all of this package's children
110f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //the associated keys are all lowercase
111f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        private RadixTree<FileSystemEntry> children = new RadixTreeImpl<FileSystemEntry>();
112f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
113f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        public PackageNameEntry(File parent, String name) {
114f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            super(new File(parent, name));
115f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        }
116f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
117f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        public PackageNameEntry(File path) {
118f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            super(path);
119f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        }
120f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
121f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        @Override
122f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        public File addUniqueChild(String[] pathElements, int pathElementsIndex) {
123f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            String elementName;
124f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            String elementNameLower;
125f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
126f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            if (pathElementsIndex == pathElements.length - 1) {
127f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                elementName = pathElements[pathElementsIndex] + fileExtension;
128f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            } else {
129f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                elementName = pathElements[pathElementsIndex];
130f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            }
131f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            elementNameLower = elementName.toLowerCase();
132f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
133f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            FileSystemEntry existingEntry = children.find(elementNameLower);
134f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            if (existingEntry != null) {
135f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                FileSystemEntry virtualEntry = existingEntry;
136f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                //if there is already another entry with the same name but different case, we need to
137f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                //add a virtual group, and then add the existing entry and the new entry to that group
138f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                if (!(existingEntry instanceof VirtualGroupEntry)) {
139f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                    if (existingEntry.file.getName().equals(elementName)) {
140f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                        if (pathElementsIndex == pathElements.length - 1) {
141f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                            return existingEntry.file;
142f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                        } else {
143f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                            return existingEntry.addUniqueChild(pathElements, pathElementsIndex + 1);
144f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                        }
145f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                    } else {
146f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                        virtualEntry = existingEntry.makeVirtual(file);
147f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                        children.replace(elementNameLower, virtualEntry);
148f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                    }
149f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                }
150f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
151f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                return virtualEntry.addUniqueChild(pathElements, pathElementsIndex);
152f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            }
153f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
154f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            if (pathElementsIndex == pathElements.length - 1) {
155f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                ClassNameEntry classNameEntry = new ClassNameEntry(file, elementName);
156f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                children.insert(elementNameLower, classNameEntry);
157f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                return classNameEntry.file;
158f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            } else {
159f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                PackageNameEntry packageNameEntry = new PackageNameEntry(file, elementName);
160f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                children.insert(elementNameLower, packageNameEntry);
161f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com                return packageNameEntry.addUniqueChild(pathElements, pathElementsIndex + 1);
162f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com            }
163f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        }
164f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    }
165f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
166f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    /**
167f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com     * A virtual group that groups together file system entries with the same name, differing only in case
168f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com     */
169f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com    private class VirtualGroupEntry extends FileSystemEntry {
170f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //this contains the FileSystemEntries for all of the files/directories in this group
171f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //the key is the unmodified name of the entry, before it is modified to be made unique (if needed).
172f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        private RadixTree<FileSystemEntry> groupEntries = new RadixTreeImpl<FileSystemEntry>();
173f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com
174f8d1aee2526a384a570b082b17f3a19fe72bd15ebungeman@google.com        //whether the containing directory is case sensitive or not.
175        //-1 = unset
176        //0 = false;
177        //1 = true;
178        private int isCaseSensitive = -1;
179
180        public VirtualGroupEntry(FileSystemEntry firstChild, File parent) {
181            super(parent);
182
183            //use the name of the first child in the group as-is
184            groupEntries.insert(firstChild.file.getName(), firstChild);
185        }
186
187        @Override
188        public File addUniqueChild(String[] pathElements, int pathElementsIndex) {
189            String elementName = pathElements[pathElementsIndex];
190
191            if (pathElementsIndex == pathElements.length - 1) {
192                elementName = elementName + fileExtension;
193            }
194
195            FileSystemEntry existingEntry = groupEntries.find(elementName);
196            if (existingEntry != null) {
197                if (pathElementsIndex == pathElements.length - 1) {
198                    return existingEntry.file;
199                } else {
200                    return existingEntry.addUniqueChild(pathElements, pathElementsIndex+1);
201                }
202            }
203
204
205            if (pathElementsIndex == pathElements.length - 1) {
206                String fileName;
207                if (!isCaseSensitive()) {
208                    fileName = pathElements[pathElementsIndex] + "." + (groupEntries.getSize()+1) + fileExtension;
209                } else {
210                    fileName = elementName;
211                }
212
213                ClassNameEntry classNameEntry = new ClassNameEntry(file, fileName);
214                groupEntries.insert(elementName, classNameEntry);
215                return classNameEntry.file;
216            } else {
217                String fileName;
218                if (!isCaseSensitive()) {
219                    fileName = pathElements[pathElementsIndex] + "." + (groupEntries.getSize()+1);
220                } else {
221                    fileName = elementName;
222                }
223
224                PackageNameEntry packageNameEntry = new PackageNameEntry(file, fileName);
225                groupEntries.insert(elementName, packageNameEntry);
226                return packageNameEntry.addUniqueChild(pathElements, pathElementsIndex + 1);
227            }
228        }
229
230        private boolean isCaseSensitive() {
231            if (isCaseSensitive != -1) {
232                return isCaseSensitive == 1;
233            }
234
235            File path = file;
236
237            if (path.exists() && path.isFile()) {
238                path = path.getParentFile();
239            }
240
241            if ((!file.exists() && !file.mkdirs())) {
242                return false;
243            }
244
245            try {
246                boolean result = testCaseSensitivity(path);
247                isCaseSensitive = result?1:0;
248                return result;
249            } catch (IOException ex) {
250                return false;
251            }
252        }
253
254        private boolean testCaseSensitivity(File path) throws IOException {
255            int num = 1;
256            File f, f2;
257            do {
258                f = new File(path, "test." + num);
259                f2 = new File(path, "TEST." + num++);
260            } while(f.exists() || f2.exists());
261
262            try {
263                try {
264                    FileWriter writer = new FileWriter(f);
265                    writer.write("test");
266                    writer.flush();
267                    writer.close();
268                } catch (IOException ex) {
269                    try {f.delete();} catch (Exception ex2) {}
270                    throw ex;
271                }
272
273                if (f2.exists()) {
274                    return false;
275                }
276
277                if (f2.createNewFile()) {
278                    return true;
279                }
280
281                //the above 2 tests should catch almost all cases. But maybe there was a failure while creating f2
282                //that isn't related to case sensitivity. Let's see if we can open the file we just created using
283                //f2
284                try {
285                    CharBuffer buf = CharBuffer.allocate(32);
286                    FileReader reader = new FileReader(f2);
287
288                    while (reader.read(buf) != -1 && buf.length() < 4);
289                    if (buf.length() == 4 && buf.toString().equals("test")) {
290                        return false;
291                    } else {
292                        //we probably shouldn't get here. If the filesystem was case-sensetive, creating a new
293                        //FileReader should have thrown a FileNotFoundException. Otherwise, we should have opened
294                        //the file and read in the string "test". It's remotely possible that someone else modified
295                        //the file after we created it. Let's be safe and return false here as well
296                        assert(false);
297                        return false;
298                    }
299                } catch (FileNotFoundException ex) {
300                    return true;
301                }
302            } finally {
303                try { f.delete(); } catch (Exception ex) {}
304                try { f2.delete(); } catch (Exception ex) {}
305            }
306        }
307
308        @Override
309        public FileSystemEntry makeVirtual(File parent) {
310            return this;
311        }
312    }
313
314    private class ClassNameEntry extends FileSystemEntry {
315        public ClassNameEntry(File parent, String name) {
316            super(new File(parent, name));
317        }
318
319        @Override
320        public File addUniqueChild(String[] pathElements, int pathElementsIndex) {
321            assert false;
322            return file;
323        }
324    }
325}
326