View Javadoc

1   /*
2    * ModeShape (http://www.modeshape.org)
3    * See the COPYRIGHT.txt file distributed with this work for information
4    * regarding copyright ownership.  Some portions may be licensed
5    * to Red Hat, Inc. under one or more contributor license agreements.
6    * See the AUTHORS.txt file in the distribution for a full listing of 
7    * individual contributors. 
8    *
9    * ModeShape is free software. Unless otherwise indicated, all code in ModeShape
10   * is licensed to you under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation; either version 2.1 of
12   * the License, or (at your option) any later version.
13   *
14   * ModeShape is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   * Lesser General Public License for more details.
18   *
19   * You should have received a copy of the GNU Lesser General Public
20   * License along with this software; if not, write to the Free
21   * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
22   * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
23   */
24  package org.modeshape.maven;
25  
26  import java.net.MalformedURLException;
27  import java.net.URL;
28  import java.net.URLStreamHandler;
29  import java.util.regex.Matcher;
30  import java.util.regex.Pattern;
31  import org.modeshape.common.text.TextDecoder;
32  import org.modeshape.common.text.TextEncoder;
33  import org.modeshape.common.text.UrlEncoder;
34  
35  /**
36   * Wrapper for a URL that uses a format for referencing JCR nodes and content.
37   */
38  public class MavenUrl {
39  
40      public static final int NO_PORT = -1;
41      public static final String JCR_PROTOCOL = "jcr";
42      protected static final String URL_PATH_DELIMITER = "/";
43  
44      private String hostname = "";
45      private int port = NO_PORT;
46      private String workspaceName = "";
47      private String path = URL_PATH_DELIMITER;
48  
49      /**
50       * Get the host name
51       * 
52       * @return the host name
53       */
54      public String getHostname() {
55          return this.hostname;
56      }
57  
58      /**
59       * @param hostname the new host name
60       */
61      public void setHostname( String hostname ) {
62          this.hostname = hostname != null ? hostname.trim() : "";
63          this.hostname = trimDelimiters(this.hostname, true, true);
64      }
65  
66      /**
67       * Get the port. This method returns {@link #NO_PORT} if the port has not been specified.
68       * 
69       * @return the port
70       */
71      public int getPort() {
72          return this.port;
73      }
74  
75      /**
76       * @param port the new port, or {@link #NO_PORT} if there is no port
77       */
78      public void setPort( int port ) {
79          this.port = port;
80      }
81  
82      public String getHostnameAndPort() {
83          if (this.port == NO_PORT) return this.hostname;
84          if (this.hostname.length() == 0) return "";
85          return this.hostname + ":" + this.port;
86      }
87  
88      /**
89       * @return workspaceName
90       */
91      public String getWorkspaceName() {
92          return this.workspaceName;
93      }
94  
95      /**
96       * Set the name of the workspace.
97       * 
98       * @param workspaceName the name of the workspace
99       */
100     public void setWorkspaceName( String workspaceName ) {
101         this.workspaceName = workspaceName != null ? workspaceName.trim() : "";
102         this.workspaceName = trimDelimiters(this.workspaceName, true, true);
103     }
104 
105     protected String trimDelimiters( String string,
106                                      boolean removeLeading,
107                                      boolean removeTrailing ) {
108         if (string == null || string.length() == 0) return "";
109         if (removeLeading) string = string.replaceAll("^/+", "");
110         if (removeTrailing) string = string.replaceAll("/+$", "");
111         return string;
112     }
113 
114     /**
115      * @return path
116      */
117     public String getPath() {
118         return this.path;
119     }
120 
121     /**
122      * @param path Sets path to the specified value.
123      */
124     public void setPath( String path ) {
125         // Make sure the path starts with a '/' ...
126         this.path = path != null ? URL_PATH_DELIMITER + path.trim() : URL_PATH_DELIMITER;
127         this.path = this.path.replaceAll("^/{2,}", "/");
128         assert this.path.startsWith(URL_PATH_DELIMITER);
129     }
130 
131     /**
132      * Get a URL that corresponds to the information in this object.
133      * 
134      * @param handler the URL stream handler that will be used to handle obtaining an input stream or an output stream on the
135      *        resulting URL
136      * @param encoder an encoder that will be used to escape any characters that are not allowed in URLs; {@link UrlEncoder} will
137      *        be used if no encoder is specified
138      * @return the URL
139      * @throws MalformedURLException if the resulting URL would be malformed
140      */
141     public URL getUrl( URLStreamHandler handler,
142                        TextEncoder encoder ) throws MalformedURLException {
143         if (encoder == null) {
144             encoder = new UrlEncoder().setSlashEncoded(false);
145         }
146         final boolean hasWorkspaceName = this.workspaceName.length() > 0;
147         final boolean hasPath = this.path.length() > 1; // path includes leading delim
148         String filePart = null;
149         if (hasWorkspaceName && hasPath) {
150             filePart = URL_PATH_DELIMITER + encoder.encode(this.workspaceName) + encoder.encode(this.path);
151         } else if (hasWorkspaceName) {
152             filePart = URL_PATH_DELIMITER + encoder.encode(this.workspaceName) + URL_PATH_DELIMITER;
153         } else if (hasPath) {
154             filePart = URL_PATH_DELIMITER + encoder.encode(this.path);
155         } else {
156             filePart = URL_PATH_DELIMITER;
157         }
158         int actualPort = this.hostname.length() != 0 ? this.port : NO_PORT;
159         return new URL(JCR_PROTOCOL, this.hostname, actualPort, filePart, handler);
160     }
161 
162     /**
163      * {@inheritDoc}
164      */
165     @Override
166     public String toString() {
167         UrlEncoder encoder = new UrlEncoder().setSlashEncoded(false);
168         String encodedWorkspace = encoder.encode(this.workspaceName);
169         String encodedPath = encoder.encode(this.path);
170         final boolean hasHostname = this.hostname.length() > 0;
171         final boolean hasPath = encodedPath.length() > 1; // path includes leading delim
172         StringBuilder sb = new StringBuilder();
173         sb.append(JCR_PROTOCOL).append(":");
174         if (hasHostname) {
175             sb.append("//").append(this.hostname);
176             if (this.port != NO_PORT) {
177                 sb.append(":").append(this.port);
178             }
179         }
180         sb.append(URL_PATH_DELIMITER).append(encodedWorkspace);
181         if (hasPath) {
182             sb.append(encodedPath);
183         } else {
184             sb.append(URL_PATH_DELIMITER);
185         }
186         return sb.toString();
187     }
188 
189     /**
190      * Parse the supplied URL and determine if the URL fits the JCR URL format. If it does, return a {@link MavenUrl} instance;
191      * otherwise return null. If the URL is malformed or otherwise invalid, this method also returns null.
192      * <p>
193      * The URL format is expected to fit the following pattern:
194      * 
195      * <pre>
196      *    jcr://hostname:port/workspaceName/path/to/node
197      * </pre>
198      * 
199      * where
200      * <ul>
201      * <li><b>hostname</b> is the name of the repository's host; typically, this is unspecified to refer to a repository in the
202      * same VM</li>
203      * <li><b>port</b> is the port on the host. If the hostname is unspecified, the port should be excluded.</li>
204      * <li><b>workspaceName</b> is the name of the workspace in the repository</li>
205      * <li><b>path/to/node</b> is the path of the node or property that is to be referenced</li>
206      * </ul>
207      * </p>
208      * 
209      * @param url the URL to be parsed
210      * @param decoder the text encoder that should be used to decode the URL; may be null if no decoding should be done
211      * @return the object representing the JCR information contained in the URL
212      * @see #parse(URL, TextDecoder)
213      */
214     public static MavenUrl parse( String url,
215                                   TextDecoder decoder ) {
216         if (decoder == null) decoder = new UrlEncoder();
217         // This regular expression has the following groups:
218         // 1) //hostname:port
219         // 2) hostname:port
220         // 3) hostname
221         // 4) :port
222         // 5) port
223         // 6) workspaceName
224         // 7) path, including leading '/'
225         Pattern urlPattern = Pattern.compile("jcr:(//(([^/:]*)(:([^/]*))?))?/([^/]*)(/?.*)");
226         Matcher matcher = urlPattern.matcher(url);
227         MavenUrl result = null;
228         if (matcher.find()) {
229             result = new MavenUrl();
230             result.setHostname(matcher.group(3));
231             String portStr = matcher.group(5);
232             if (portStr != null && portStr.trim().length() != 0) {
233                 result.setPort(Integer.parseInt(portStr));
234             }
235             String workspaceName = decoder.decode(matcher.group(6));
236             String path = decoder.decode(matcher.group(7));
237             result.setWorkspaceName(workspaceName);
238             result.setPath(path);
239         }
240         return result;
241     }
242 
243     /**
244      * Parse the supplied URL and determine if the URL fits the JCR URL format. If it does, return a {@link MavenUrl} instance;
245      * otherwise return null. If the URL is malformed or otherwise invalid, this method also returns null.
246      * 
247      * @param url the URL to be parsed
248      * @param decoder the text encoder that should be used to decode the URL; may be null if no decoding should be done
249      * @return the object representing the JCR information contained in the URL
250      * @see #parse(String,TextDecoder)
251      */
252     public static MavenUrl parse( URL url,
253                                   TextDecoder decoder ) {
254         if (url == null) return null;
255         return parse(url.toExternalForm(), decoder);
256     }
257 }