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