View Javadoc
1   /*
2    * Copyright (C) 2005-2015 Schlichtherle IT Services.
3    * All rights reserved. Use is subject to license terms.
4    */
5   package net.java.truevfs.kernel.spec;
6   
7   import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
8   import net.java.truecommons.shed.QuotedUriSyntaxException;
9   import net.java.truecommons.shed.UriBuilder;
10  
11  import javax.annotation.concurrent.Immutable;
12  import java.net.URI;
13  import java.net.URISyntaxException;
14  
15  import static net.java.truevfs.kernel.spec.FsNodeName.SEPARATOR;
16  import static net.java.truevfs.kernel.spec.FsNodeName.SEPARATOR_CHAR;
17  
18  /**
19   * Modifies a URI when parsing a file system {@linkplain FsNodePath node path},
20   * a {@linkplain FsMountPoint mount point} or an
21   * {@linkplain FsNodeName node name}.
22   * 
23   * @author Christian Schlichtherle
24   */
25  @Immutable
26  public enum FsUriModifier {
27  
28      /**
29       * The null modifier does nothing but ensure that the URI path is in normal
30       * form.
31       */
32      NULL {
33          @Override
34          URI modify(URI uri, PostFix fix) throws URISyntaxException {
35              if (uri.normalize() != uri)
36                  throw new QuotedUriSyntaxException(uri, "URI path not in normal form");
37              return uri;
38          }
39      },
40  
41      /**
42       * The canonicalize modifier applies a {@link PostFix post-fix} which
43       * depends on the class specific URI syntax.
44       */
45      CANONICALIZE {
46          @Override
47          URI modify(URI uri, PostFix fix) throws URISyntaxException {
48              return fix.modify(uri);
49          }
50      };
51  
52      /**
53       * An idempotent function which modifies a URI.
54       *
55       * @param  uri the URI to modify.
56       * @param  fix the post-fix to apply if required.
57       * @return the modified URI.
58       */
59      abstract URI modify(URI uri, PostFix fix) throws URISyntaxException;
60  
61      /**
62       * Post-fixes a URI when it gets
63       * {@link FsUriModifier#CANONICALIZE canonicalized}.
64       */
65      public enum PostFix {
66  
67          /**
68           * The post-fix for an {@link FsNodePath} depends on the given URI:
69           * If the URI is opaque or not absolute or has a fragment component
70           * defined, nothing is modified.
71           * Otherwise, the following modifications are conducted:
72           * <ol>
73           * <li>If the URI path component starts with two separators, the
74           *     substring following until the next separator is moved to the
75           *     authority part of the URI.
76           *     This behavior is intended to fix URIs returned by
77           *     {@link java.io.File#toURI()}.
78           * <li>The URI path component gets truncated so that it does not end
79           *     with {@link FsNodeName#SEPARATOR} whereby a trailing separator
80           *     after a Windows-like drive letter is preserved.
81           * <li>An empty authority component in the scheme specific part gets
82           *     truncated.
83           * </ol>
84           * <p>
85           * Note that this fix is not limited to Windows in order to make
86           * this function work identically on all platforms.
87           */
88          NODE_PATH {
89              @Override
90              @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
91              URI modify(URI uri) throws URISyntaxException {
92                  if (uri.isOpaque()
93                          || !uri.isAbsolute()
94                          || null != uri.getRawFragment())
95                      return uri;
96                  {
97                      String a = uri.getRawAuthority();
98                      String p = uri.getRawPath();
99                      if (null == a && null != p && p.startsWith(TWO_SEPARATORS)) {
100                         int i = p.indexOf(SEPARATOR_CHAR, 2);
101                         if (2 <= i) {
102                             a = p.substring(2, i);
103                             p = p.substring(i);
104                         }
105                         uri = new UriBuilder(uri, true).authority(a).path(p).getUri();
106                     }
107                     uri = uri.normalize();
108                 }
109                 String s = uri.getScheme();
110                 String a = uri.getRawAuthority();
111                 String p = uri.getRawPath(), q = p;
112                 for (int l; p.endsWith(SEPARATOR)
113                         && (   1 <=(l = p.length()) && null == s
114                             || 2 <= l && ':' != p.charAt(l - 2)
115                             || 3 <= l && !p.startsWith(SEPARATOR)
116                             || 4 <  l && p.startsWith(SEPARATOR)
117                             || null != a); )
118                     p = p.substring(0, l - 1);
119                 return p != q
120                         ? new UriBuilder(uri, true).path(p).getUri()
121                         : uri;
122             }
123         },
124 
125         /**
126          * The post-fix for an {@link FsMountPoint} just normalizes the given
127          * URI.
128          */
129         MOUNT_POINT {
130             @Override
131             URI modify(URI uri) {
132                 return uri.normalize();
133             }
134         },
135 
136         /**
137          * The post-fix for an {@link FsNodeName} depends on the given URI:
138          * If the URI is absolute or has an authority or a fragment component
139          * defined, nothing is modified.
140          * Otherwise, the URI path component gets truncated so that it does not
141          * start or end with {@link FsNodeName#SEPARATOR}.
142          */
143         NODE_NAME {
144             @Override
145             @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
146             URI modify(URI uri) throws URISyntaxException {
147                 uri = uri.normalize();
148                 if (uri.isAbsolute()
149                         || null != uri.getRawAuthority()
150                         || null != uri.getRawFragment())
151                     return uri;
152                 String p = uri.getRawPath(), q = p;
153                 while (p.startsWith(SEPARATOR))
154                     p = p.substring(1);
155                 while (p.endsWith(SEPARATOR))
156                     p = p.substring(0, p.length() - 1);
157                 return p == q
158                         ? uri
159                         : new UriBuilder(uri, true).path(p).getUri();
160             }
161         };
162 
163         /**
164          * An idempotent function which modifies the given URI.
165          *
166          * @param  uri the URI to modify.
167          * @return the modified URI.
168          */
169         abstract URI modify(URI uri) throws URISyntaxException;
170 
171         private static final String TWO_SEPARATORS = SEPARATOR + SEPARATOR;
172     }
173 }