Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
URIPathDescriptor |
|
| 2.8461538461538463;2,846 |
1 | package org.apache.maven.doxia.site.decoration.inheritance; | |
2 | ||
3 | /* | |
4 | * Licensed to the Apache Software Foundation (ASF) under one | |
5 | * or more contributor license agreements. See the NOTICE file | |
6 | * distributed with this work for additional information | |
7 | * regarding copyright ownership. The ASF licenses this file | |
8 | * to you under the Apache License, Version 2.0 (the | |
9 | * "License"); you may not use this file except in compliance | |
10 | * with the License. You may obtain a copy of the License at | |
11 | * | |
12 | * http://www.apache.org/licenses/LICENSE-2.0 | |
13 | * | |
14 | * Unless required by applicable law or agreed to in writing, | |
15 | * software distributed under the License is distributed on an | |
16 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
17 | * KIND, either express or implied. See the License for the | |
18 | * specific language governing permissions and limitations | |
19 | * under the License. | |
20 | */ | |
21 | ||
22 | import java.net.URI; | |
23 | import java.net.URISyntaxException; | |
24 | ||
25 | import org.codehaus.plexus.util.PathTool; | |
26 | ||
27 | /** | |
28 | * Describes a link that may be absolute or relative, and that is anchored to an absolute URI. | |
29 | * | |
30 | * @author ltheussl | |
31 | * | |
32 | * @since 1.2 | |
33 | */ | |
34 | public class URIPathDescriptor | |
35 | { | |
36 | private final URI baseURI; | |
37 | private final URI link; | |
38 | ||
39 | /** | |
40 | * A URIPathDescriptor consists of a base URI and a link. | |
41 | * Both arguments to this constructor have to be parsable to URIs. | |
42 | * The baseURI parameter has to be absolute in the sense of {@link URI#isAbsolute()}. | |
43 | * | |
44 | * Before being parsed to {@link URI}s, the arguments are modified to catch | |
45 | * some common bad practices: first all Windows-style backslashes '\' are replaced by | |
46 | * forward slashes '/'. | |
47 | * If the baseURI does not end with '/', a slash is appended. | |
48 | * If the link starts with a '/', the first character is stripped. | |
49 | * | |
50 | * @param baseURI The base URI. Has to be a valid absolute URI. | |
51 | * In addition, the path of the URI should not have any file part, | |
52 | * ie <code>http://maven.apache.org/</code> is valid, | |
53 | * <code>http://maven.apache.org/index.html</code> is not. | |
54 | * Even though the latter form is accepted without warning, | |
55 | * the methods in this class will not return what is probably expected, | |
56 | * because a slash is appended during construction, as noted above. | |
57 | * @param link the link. This may be a relative link or an absolute link. | |
58 | * Note that URIs that start with a "/", ie don't specify a scheme, are considered relative. | |
59 | * | |
60 | * @throws IllegalArgumentException if either argument is not parsable as a URI, | |
61 | * or if baseURI is not absolute. | |
62 | */ | |
63 | public URIPathDescriptor( final String baseURI, final String link ) | |
64 | 1770 | { |
65 | 1770 | final String llink = sanitizeLink( link ); |
66 | 1770 | final String bbase = sanitizeBase( baseURI ); |
67 | ||
68 | 1770 | this.baseURI = URI.create( bbase ).normalize(); |
69 | 1764 | this.link = URI.create( llink ).normalize(); |
70 | ||
71 | 1758 | if ( !this.baseURI.isAbsolute() ) |
72 | { | |
73 | 6 | throw new IllegalArgumentException( "Base URI is not absolute: " + baseURI ); |
74 | } | |
75 | 1752 | } |
76 | ||
77 | /** | |
78 | * Return the base of this URIPathDescriptor as a URI. | |
79 | * This is always {@link URI#normalize() normalized}. | |
80 | * | |
81 | * @return the normalized base URI. | |
82 | */ | |
83 | public URI getBaseURI() | |
84 | { | |
85 | 12 | return baseURI; |
86 | } | |
87 | ||
88 | /** | |
89 | * Return the link of this URIPathDescriptor as a URI. | |
90 | * This is always {@link URI#normalize() normalized}. | |
91 | * | |
92 | * @return the normalized link URI. | |
93 | */ | |
94 | public URI getLink() | |
95 | { | |
96 | 6 | return link; |
97 | } | |
98 | ||
99 | /** | |
100 | * Resolve the link to the base. | |
101 | * This always returns an absolute URI. If link is absolute, link is returned. | |
102 | * | |
103 | * @return the resolved link. This is equivalent to calling | |
104 | * {@link #getBaseURI()}.{@link URI#resolve(java.net.URI) resolve}( {@link #getLink()} ). | |
105 | */ | |
106 | public URI resolveLink() | |
107 | { | |
108 | 252 | return baseURI.resolve( link ); |
109 | } | |
110 | ||
111 | /** | |
112 | * Calculate the relative link with respect to the base. | |
113 | * The original link is returned if either | |
114 | * link is relative; | |
115 | * or link and base do not share the {@link #sameSite(java.net.URI) same site}. | |
116 | * | |
117 | * @return the link as a relative URI. | |
118 | */ | |
119 | public URI relativizeLink() | |
120 | { | |
121 | 462 | return relativizeLink( baseURI.toString(), link ); |
122 | } | |
123 | ||
124 | // NOTE: URI.relativize does not work as expected, see | |
125 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6226081 | |
126 | private static URI relativizeLink( final String base, final URI link ) | |
127 | { | |
128 | 462 | if ( !link.isAbsolute() ) |
129 | { | |
130 | 168 | return link; |
131 | } | |
132 | ||
133 | 294 | final URI newBaseURI = URI.create( base ); |
134 | ||
135 | 294 | if ( !sameSite( link, newBaseURI ) ) |
136 | { | |
137 | 66 | return link; |
138 | } | |
139 | ||
140 | 228 | final String relativePath = PathTool.getRelativeWebPath( newBaseURI.toString(), link.toString() ); |
141 | ||
142 | 228 | return URI.create( correctRelativePath( relativePath ) ); |
143 | } | |
144 | ||
145 | /** | |
146 | * Calculate the link as viewed from a different base. | |
147 | * This returns the original link if link is absolute. | |
148 | * This returns {@link #resolveLink()} if either | |
149 | * newBase == null, | |
150 | * or newBase is not parsable as a URI, | |
151 | * or newBase and this {@link #getBaseURI()} do not share the | |
152 | * {@link #sameSite(java.net.URI) same site}. | |
153 | * | |
154 | * @param newBase the new base URI. Has to be parsable as a URI. | |
155 | *. | |
156 | * @return a new relative link or the original link {@link #resolveLink() resolved}, | |
157 | * i.e. as an absolute link, if the link cannot be re-based. | |
158 | */ | |
159 | public URI rebaseLink( final String newBase ) | |
160 | { | |
161 | 1164 | if ( link.isAbsolute() ) |
162 | { | |
163 | 420 | return link; |
164 | } | |
165 | ||
166 | 744 | if ( newBase == null ) |
167 | { | |
168 | 6 | return resolveLink(); |
169 | } | |
170 | ||
171 | final URI newBaseURI; | |
172 | ||
173 | try | |
174 | { | |
175 | 738 | newBaseURI = new URI( newBase ); |
176 | } | |
177 | 6 | catch ( URISyntaxException ex ) |
178 | { | |
179 | 6 | return resolveLink(); |
180 | 732 | } |
181 | ||
182 | 732 | if ( !sameSite( newBaseURI ) ) |
183 | { | |
184 | 114 | return resolveLink(); |
185 | } | |
186 | ||
187 | 618 | final String relativeBasePath = PathTool.getRelativeWebPath( newBaseURI.getPath(), baseURI.getPath() ); |
188 | ||
189 | 618 | return URI.create( correctRelativePath( relativeBasePath ) ).resolve( link ); |
190 | } | |
191 | ||
192 | private static String correctRelativePath( final String relativePath ) | |
193 | { | |
194 | 846 | if ( "".equals( relativePath ) || "/".equals( relativePath ) ) |
195 | { | |
196 | 42 | return "./"; |
197 | } | |
198 | else | |
199 | { | |
200 | 804 | return relativePath; |
201 | } | |
202 | } | |
203 | ||
204 | /** | |
205 | * Check if this URIPathDescriptor lives on the same site as the given URI. | |
206 | * | |
207 | * @param uri a URI to compare with. | |
208 | * May be null, in which case false is returned. | |
209 | * | |
210 | * @return true if {@link #getBaseURI()} shares the same scheme, host and port with the given URI | |
211 | * where null values are allowed. | |
212 | */ | |
213 | public boolean sameSite( final URI uri ) | |
214 | { | |
215 | 828 | return ( uri != null ) && sameSite( this.baseURI, uri ); |
216 | } | |
217 | ||
218 | private static boolean sameSite( final URI baseURI, final URI newBaseURI ) | |
219 | { | |
220 | 1116 | final boolean sameScheme = |
221 | ( newBaseURI.getScheme() == null ? false : baseURI.getScheme().equalsIgnoreCase( newBaseURI.getScheme() ) ); | |
222 | 1116 | final boolean sameHost = |
223 | ( baseURI.getHost() == null ? newBaseURI.getHost() == null | |
224 | : baseURI.getHost().equalsIgnoreCase( newBaseURI.getHost() ) ); | |
225 | 1116 | final boolean samePort = ( baseURI.getPort() == newBaseURI.getPort() ); |
226 | ||
227 | 1116 | return ( sameScheme && samePort && sameHost ); |
228 | } | |
229 | ||
230 | /** | |
231 | * Construct a string representation of this URIPathDescriptor. | |
232 | * This is equivalent to calling {@link #resolveLink()}.toString(). | |
233 | * | |
234 | * @return this URIPathDescriptor as a String. | |
235 | */ | |
236 | public String toString() | |
237 | { | |
238 | 66 | return resolveLink().toString(); |
239 | } | |
240 | ||
241 | private static String sanitizeBase( final String base ) | |
242 | { | |
243 | 1770 | String sane = base.replace( '\\', '/' ); |
244 | ||
245 | 1770 | if ( !sane.endsWith( "/" ) ) |
246 | { | |
247 | 1098 | sane += "/"; |
248 | } | |
249 | ||
250 | 1770 | return sane; |
251 | } | |
252 | ||
253 | private static String sanitizeLink( final String link ) | |
254 | { | |
255 | 1770 | String sane = link.replace( '\\', '/' ); |
256 | ||
257 | 1770 | if ( sane.startsWith( "/" ) ) |
258 | { | |
259 | 390 | sane = sane.substring( 1 ); |
260 | } | |
261 | ||
262 | 1770 | return sane; |
263 | } | |
264 | } |