View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.rat.document.impl;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.nio.file.Files;
24  import java.nio.file.Path;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.List;
28  import java.util.Objects;
29  
30  import org.apache.commons.io.FileUtils;
31  import org.apache.commons.lang3.StringUtils;
32  import org.apache.rat.utils.DefaultLog;
33  
34  
35  /**
36   * A collection of names for a document. All names in the set are
37   * either Rat (Linux like) or native (OS specific).
38   */
39  public final class DocumentName implements Comparable<DocumentName> {
40      /** True if the file system on which we are operating is case-sensitive */
41      public static final boolean FS_IS_CASE_SENSITIVE;
42      /** The full name for the document */
43      private final String name;
44      /** The name of the base directory for the document */
45      private final String baseName;
46      /** The directory separator for this document. */
47      private final String dirSeparator;
48      /** The case-sensitive flag */
49      private final boolean isCaseSensitive;
50  
51      static {
52          boolean fsSensitive = true;
53          File f = null;
54          try {
55              Path p = Files.createTempDirectory("NameSet");
56              f = p.toFile();
57              fsSensitive = !new File(f, "a").equals(new File(f, "A"));
58          } catch (IOException e) {
59              fsSensitive = true;
60          } finally {
61              if (f != null) {
62                  try {
63                      FileUtils.deleteDirectory(f);
64                  } catch (IOException e) {
65                      DefaultLog.getInstance().warn("Unable to delete temporary directory: " + f, e);
66                  }
67              }
68          }
69          FS_IS_CASE_SENSITIVE = fsSensitive;
70      }
71  
72      /**
73       * Creates a Document name.
74       * @param name the name of the document
75       * @param baseName the base name of the document.
76       * @param dirSeparator the directory separator used in the name.
77       * @param isCaseSensitive {@code true} if the name is case-sensitive.
78       */
79      public DocumentName(final String name, final String baseName, final String dirSeparator, final boolean isCaseSensitive) {
80          this.name = Objects.requireNonNull(name);
81          this.baseName = Objects.requireNonNull(StringUtils.defaultIfEmpty(baseName, null));
82          this.dirSeparator = Objects.requireNonNull(dirSeparator);
83          this.isCaseSensitive = isCaseSensitive;
84      }
85  
86      /**
87       * Creates a document name with the name of the file and the same basename as the baseName document.
88       * @param file the file to name the document from.
89       * @param baseName the DocumentName to provide the baseName.
90       */
91      public DocumentName(final File file, final DocumentName baseName) {
92          this(file.getAbsolutePath(), baseName.baseName, File.separator, FS_IS_CASE_SENSITIVE);
93      }
94  
95      /**
96       * Creates a document name with the name and basename equal to the file name
97       * @param file the file name to use.
98       */
99      public DocumentName(final File file) {
100         this(file.getAbsolutePath(), file.getAbsolutePath(), File.separator, FS_IS_CASE_SENSITIVE);
101     }
102 
103     /**
104      * Creates a new document name by adding the child to the current name.
105      * @param child the child to add (must use directory separator from this document name).
106      * @return the new document name with the same base name, directory separator and case sensitivity as this one.
107      */
108     public DocumentName resolve(final String child) {
109         List<String> parts = new ArrayList<>();
110         parts.addAll(Arrays.asList(tokenize(name)));
111         parts.addAll(Arrays.asList(tokenize(child)));
112         String newName = String.join(dirSeparator, parts);
113         return new DocumentName(newName, baseName, dirSeparator, isCaseSensitive);
114     }
115 
116     /**
117      * Gets the fully qualified name of the document.
118      * @return the fully qualified name of the document.
119      */
120     public String getName() {
121         return name;
122     }
123 
124     /**
125      * Gets the fully qualified basename of the document.
126      * @return the fully qualified basename of the document.
127      */
128     public String getBaseName() {
129         return baseName;
130     }
131 
132     /**
133      * Gets the DocumentName for the basename of this document name.
134      * @return the DocumentName for the basename of this document name.
135      */
136     public DocumentName getBaseDocumentName() {
137         return name.equals(baseName) ? this : new DocumentName(baseName, baseName, dirSeparator, isCaseSensitive);
138     }
139 
140     /**
141      * Returns the directory separator.
142      * @return the directory separator.
143      */
144     public String getDirectorySeparator() {
145         return dirSeparator;
146     }
147 
148     /**
149      * Gets the portion of the name that is not part of the base name.
150      * The resulting name will always start with the directory separator.
151      * @return the portion of the name that is not part of the base name.
152      */
153     public String localized() {
154         String result = name;
155         if (result.startsWith(baseName)) {
156             result = result.substring(baseName.length());
157         }
158         if (!result.startsWith(dirSeparator)) {
159             result = dirSeparator + result;
160         }
161         return result;
162     }
163 
164     /**
165      * Gets the portion of the name that is not part of the base name.
166      * The resulting name will always start with the directory separator.
167      * @param dirSeparator The character to use to separate directories in the result.
168      * @return the portion of the name that is not part of the base name.
169      */
170     public String localized(final String dirSeparator) {
171         return String.join(dirSeparator, tokenize(localized()));
172     }
173 
174     /**
175      * Tokenizes the string based on the dirSeparator
176      * @param source the source to tokenize
177      * @return the array of tokenized strings.
178      */
179     public String[] tokenize(final String source) {
180         return source.split("\\Q" + dirSeparator + "\\E");
181     }
182 
183     /**
184      * Gets the last segment of the name. This is the part after the last directory separator.
185      * @return the last segment of the name.
186      */
187     public String getShortName() {
188         int pos = name.lastIndexOf(dirSeparator);
189         return pos == -1 ? name : name.substring(pos + 1);
190     }
191 
192     /**
193      * Gets the case sensitivity flag.
194      * @return {@code true} if the name is case-sensitive.
195      */
196     public boolean isCaseSensitive() {
197         return isCaseSensitive;
198     }
199 
200     @Override
201     public String toString() {
202         return localized();
203     }
204 
205     @Override
206     public int compareTo(final DocumentName o) {
207         return FS_IS_CASE_SENSITIVE ? name.compareTo(o.name) : name.compareToIgnoreCase(o.name);
208     }
209 
210     @Override
211     public boolean equals(final Object o) {
212         if (this == o) {
213             return true;
214         }
215         if (o == null || getClass() != o.getClass()) {
216             return false;
217         }
218         DocumentName that = (DocumentName) o;
219         if (isCaseSensitive() == that.isCaseSensitive() &&  Objects.equals(dirSeparator, that.dirSeparator)) {
220             return isCaseSensitive ? name.equalsIgnoreCase(that.name) : name.equals(that.name);
221         }
222         return false;
223     }
224 
225     @Override
226     public int hashCode() {
227         return Objects.hash(name, dirSeparator, isCaseSensitive());
228     }
229 }