001 /* 002 * JBoss DNA (http://www.jboss.org/dna) 003 * See the COPYRIGHT.txt file distributed with this work for information 004 * regarding copyright ownership. Some portions may be licensed 005 * to Red Hat, Inc. under one or more contributor license agreements. 006 * See the AUTHORS.txt file in the distribution for a full listing of 007 * individual contributors. 008 * 009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA 010 * is licensed to you under the terms of the GNU Lesser General Public License as 011 * published by the Free Software Foundation; either version 2.1 of 012 * the License, or (at your option) any later version. 013 * 014 * JBoss DNA is distributed in the hope that it will be useful, 015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 017 * Lesser General Public License for more details. 018 * 019 * You should have received a copy of the GNU Lesser General Public 020 * License along with this software; if not, write to the Free 021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 023 */ 024 package org.jboss.dna.maven; 025 026 import java.net.MalformedURLException; 027 import java.net.URL; 028 import java.net.URLStreamHandler; 029 import java.util.regex.Matcher; 030 import java.util.regex.Pattern; 031 import org.jboss.dna.common.text.TextDecoder; 032 import org.jboss.dna.common.text.TextEncoder; 033 import org.jboss.dna.common.text.UrlEncoder; 034 035 /** 036 * Wrapper for a URL that uses a format for referencing JCR nodes and content. 037 * 038 * @author Randall Hauch 039 */ 040 public class MavenUrl { 041 042 public static final int NO_PORT = -1; 043 public static final String JCR_PROTOCOL = "jcr"; 044 protected static final String URL_PATH_DELIMITER = "/"; 045 046 private String hostname = ""; 047 private int port = NO_PORT; 048 private String workspaceName = ""; 049 private String path = URL_PATH_DELIMITER; 050 051 /** 052 * Get the host name 053 * 054 * @return the host name 055 */ 056 public String getHostname() { 057 return this.hostname; 058 } 059 060 /** 061 * @param hostname the new host name 062 */ 063 public void setHostname( String hostname ) { 064 this.hostname = hostname != null ? hostname.trim() : ""; 065 this.hostname = trimDelimiters(this.hostname, true, true); 066 } 067 068 /** 069 * Get the port. This method returns {@link #NO_PORT} if the port has not been specified. 070 * 071 * @return the port 072 */ 073 public int getPort() { 074 return this.port; 075 } 076 077 /** 078 * @param port the new port, or {@link #NO_PORT} if there is no port 079 */ 080 public void setPort( int port ) { 081 this.port = port; 082 } 083 084 public String getHostnameAndPort() { 085 if (this.port == NO_PORT) return this.hostname; 086 if (this.hostname.length() == 0) return ""; 087 return this.hostname + ":" + this.port; 088 } 089 090 /** 091 * @return workspaceName 092 */ 093 public String getWorkspaceName() { 094 return this.workspaceName; 095 } 096 097 /** 098 * Set the name of the workspace. 099 * 100 * @param workspaceName the name of the workspace 101 */ 102 public void setWorkspaceName( String workspaceName ) { 103 this.workspaceName = workspaceName != null ? workspaceName.trim() : ""; 104 this.workspaceName = trimDelimiters(this.workspaceName, true, true); 105 } 106 107 protected String trimDelimiters( String string, boolean removeLeading, 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, TextEncoder encoder ) throws MalformedURLException { 142 if (encoder == null) { 143 encoder = new UrlEncoder().setSlashEncoded(false); 144 } 145 final boolean hasWorkspaceName = this.workspaceName.length() > 0; 146 final boolean hasPath = this.path.length() > 1; // path includes leading delim 147 String filePart = null; 148 if (hasWorkspaceName && hasPath) { 149 filePart = URL_PATH_DELIMITER + encoder.encode(this.workspaceName) + encoder.encode(this.path); 150 } else if (hasWorkspaceName) { 151 filePart = URL_PATH_DELIMITER + encoder.encode(this.workspaceName) + URL_PATH_DELIMITER; 152 } else if (hasPath) { 153 filePart = URL_PATH_DELIMITER + encoder.encode(this.path); 154 } else { 155 filePart = URL_PATH_DELIMITER; 156 } 157 int actualPort = this.hostname.length() != 0 ? this.port : NO_PORT; 158 return new URL(JCR_PROTOCOL, this.hostname, actualPort, filePart, handler); 159 } 160 161 /** 162 * {@inheritDoc} 163 */ 164 @Override 165 public String toString() { 166 UrlEncoder encoder = new UrlEncoder().setSlashEncoded(false); 167 String encodedWorkspace = encoder.encode(this.workspaceName); 168 String encodedPath = encoder.encode(this.path); 169 final boolean hasHostname = this.hostname.length() > 0; 170 final boolean hasPath = encodedPath.length() > 1; // path includes leading delim 171 StringBuilder sb = new StringBuilder(); 172 sb.append(JCR_PROTOCOL).append(":"); 173 if (hasHostname) { 174 sb.append("//").append(this.hostname); 175 if (this.port != NO_PORT) { 176 sb.append(":").append(this.port); 177 } 178 } 179 sb.append(URL_PATH_DELIMITER).append(encodedWorkspace); 180 if (hasPath) { 181 sb.append(encodedPath); 182 } else { 183 sb.append(URL_PATH_DELIMITER); 184 } 185 return sb.toString(); 186 } 187 188 /** 189 * Parse the supplied URL and determine if the URL fits the JCR URL format. If it does, return a {@link MavenUrl} instance; 190 * otherwise return null. If the URL is malformed or otherwise invalid, this method also returns null. 191 * <p> 192 * The URL format is expected to fit the following pattern: 193 * 194 * <pre> 195 * jcr://hostname:port/workspaceName/path/to/node 196 * </pre> 197 * 198 * where 199 * <ul> 200 * <li><b>hostname</b> is the name of the repository's host; typically, this is unspecified to refer to a repository in the 201 * same VM</li> 202 * <li><b>port</b> is the port on the host. If the hostname is unspecified, the port should be excluded.</li> 203 * <li><b>workspaceName</b> is the name of the workspace in the repository</li> 204 * <li><b>path/to/node</b> is the path of the node or property that is to be referenced</li> 205 * </ul> 206 * </p> 207 * 208 * @param url the URL to be parsed 209 * @param decoder the text encoder that should be used to decode the URL; may be null if no decoding should be done 210 * @return the object representing the JCR information contained in the URL 211 * @see #parse(URL, TextDecoder) 212 */ 213 public static MavenUrl parse( String url, TextDecoder decoder ) { 214 if (decoder == null) decoder = new UrlEncoder(); 215 // This regular expression has the following groups: 216 // 1) //hostname:port 217 // 2) hostname:port 218 // 3) hostname 219 // 4) :port 220 // 5) port 221 // 6) workspaceName 222 // 7) path, including leading '/' 223 Pattern urlPattern = Pattern.compile("jcr:(//(([^/:]*)(:([^/]*))?))?/([^/]*)(/?.*)"); 224 Matcher matcher = urlPattern.matcher(url); 225 MavenUrl result = null; 226 if (matcher.find()) { 227 result = new MavenUrl(); 228 result.setHostname(matcher.group(3)); 229 String portStr = matcher.group(5); 230 if (portStr != null && portStr.trim().length() != 0) { 231 result.setPort(Integer.parseInt(portStr)); 232 } 233 String workspaceName = decoder.decode(matcher.group(6)); 234 String path = decoder.decode(matcher.group(7)); 235 result.setWorkspaceName(workspaceName); 236 result.setPath(path); 237 } 238 return result; 239 } 240 241 /** 242 * Parse the supplied URL and determine if the URL fits the JCR URL format. If it does, return a {@link MavenUrl} instance; 243 * otherwise return null. If the URL is malformed or otherwise invalid, this method also returns null. 244 * 245 * @param url the URL to be parsed 246 * @param decoder the text encoder that should be used to decode the URL; may be null if no decoding should be done 247 * @return the object representing the JCR information contained in the URL 248 * @see #parse(String,TextDecoder) 249 */ 250 public static MavenUrl parse( URL url, TextDecoder decoder ) { 251 if (url == null) return null; 252 return parse(url.toExternalForm(), decoder); 253 } 254 }