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