1 package com.atlassian.plugin.url;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.net.JarURLConnection;
6 import java.net.MalformedURLException;
7 import java.net.URL;
8 import java.net.URLDecoder;
9 import java.util.ArrayList;
10 import java.util.List;
11 import java.util.StringTokenizer;
12 import java.util.jar.JarEntry;
13 import java.util.jar.JarFile;
14 import java.util.jar.JarInputStream;
15
16
17
18 class InnerJarURLConnection extends JarURLConnection
19 {
20 private URL baseResource;
21 private String[] segments;
22 private InputStream in;
23
24 public InnerJarURLConnection( URL url ) throws IOException
25 {
26 super( url = normaliseURL( url ) );
27
28 String baseText = url.getPath();
29
30 int bangLoc = baseText.indexOf( "!" );
31
32 String baseResourceText = baseText.substring( 0, bangLoc );
33
34 String extraText = "";
35
36 if ( bangLoc <= ( baseText.length() - 2 )
37 &&
38 baseText.charAt( bangLoc + 1 ) == '/' )
39 {
40 if ( bangLoc + 2 == baseText.length() )
41 {
42 extraText = "";
43 }
44 else
45 {
46 extraText = baseText.substring( bangLoc + 1 );
47 }
48 }
49 else
50 {
51 throw new MalformedURLException( "No !/ in url: " + url.toExternalForm() );
52 }
53
54
55 List segments = new ArrayList();
56
57 StringTokenizer tokens = new StringTokenizer( extraText, "!" );
58
59 while ( tokens.hasMoreTokens() )
60 {
61 segments.add( tokens.nextToken() );
62 }
63
64 this.segments = (String[]) segments.toArray( new String[segments.size()] );
65 this.baseResource = new URL( baseResourceText );
66 }
67
68 protected static URL normaliseURL( URL url ) throws MalformedURLException
69 {
70 String text = normalizeUrlPath( url.toString() );
71
72 if ( !text.startsWith( "jar:" ) )
73 {
74 text = "jar:" + text;
75 }
76
77 if ( text.indexOf( '!' ) < 0 )
78 {
79 text = text + "!/";
80 }
81
82 return new URL( text );
83 }
84
85
86
87
88
89
90 protected String[] getSegments()
91 {
92 return this.segments;
93 }
94
95
96
97
98
99
100 protected URL getBaseResource()
101 {
102 return this.baseResource;
103 }
104
105
106
107
108 public void connect() throws IOException
109 {
110 if ( this.segments.length == 0 )
111 {
112 setupBaseResourceInputStream();
113 }
114 else
115 {
116 setupPathedInputStream();
117 }
118 }
119
120
121
122
123
124
125 protected void setupBaseResourceInputStream() throws IOException
126 {
127 this.in = getBaseResource().openStream();
128 }
129
130
131
132
133
134
135 protected void setupPathedInputStream() throws IOException
136 {
137 InputStream curIn = getBaseResource().openStream();
138
139 for ( int i = 0; i < this.segments.length; ++i )
140 {
141 curIn = getSegmentInputStream( curIn, segments[i]);
142 }
143
144 this.in = curIn;
145 }
146
147
148
149
150
151
152
153
154
155
156 protected InputStream getSegmentInputStream( InputStream baseIn, String segment) throws IOException
157 {
158 JarInputStream jarIn = new JarInputStream( baseIn );
159 JarEntry entry = null;
160
161 while ( jarIn.available() != 0 )
162 {
163 entry = jarIn.getNextJarEntry();
164
165 if ( entry == null )
166 {
167 break;
168 }
169
170 if ( ( "/" + entry.getName() ).equals( segment ) )
171 {
172 return jarIn;
173 }
174 }
175
176 throw new IOException( "unable to locate segment: " + segment );
177 }
178
179
180
181
182 public InputStream getInputStream() throws IOException
183 {
184 if ( this.in == null )
185 {
186 connect();
187 }
188 return this.in;
189 }
190
191
192
193
194
195
196 public JarFile getJarFile() throws IOException
197 {
198 String url = baseResource.toExternalForm();
199
200 if ( url.startsWith( "file:/" ) )
201 {
202 url = url.substring( 6 );
203 }
204
205 return new JarFile( URLDecoder.decode( url, "UTF-8" ) );
206 }
207
208 private static String normalizeUrlPath( String name )
209 {
210 if ( name.startsWith( "/" ) )
211 {
212 name = name.substring( 1 );
213 }
214
215
216
217
218
219 int i = name.indexOf( "/.." );
220
221
222
223 if ( i > 0 )
224 {
225 int j = name.lastIndexOf( "/", i - 1 );
226
227 name = name.substring( 0, j ) + name.substring( i + 3 );
228 }
229
230 return name;
231 }
232
233 }