Line 0
Link Here
|
|
|
1 |
/* |
2 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 |
* |
4 |
* Copyright 2008 Sun Microsystems, Inc. All rights reserved. |
5 |
* |
6 |
* The contents of this file are subject to the terms of either the GNU |
7 |
* General Public License Version 2 only ("GPL") or the Common |
8 |
* Development and Distribution License("CDDL") (collectively, the |
9 |
* "License"). You may not use this file except in compliance with the |
10 |
* License. You can obtain a copy of the License at |
11 |
* http://www.netbeans.org/cddl-gplv2.html |
12 |
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the |
13 |
* specific language governing permissions and limitations under the |
14 |
* License. When distributing the software, include this License Header |
15 |
* Notice in each file and include the License file at |
16 |
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this |
17 |
* particular file as subject to the "Classpath" exception as provided |
18 |
* by Sun in the GPL Version 2 section of the License file that |
19 |
* accompanied this code. If applicable, add the following below the |
20 |
* License Header, with the fields enclosed by brackets [] replaced by |
21 |
* your own identifying information: |
22 |
* "Portions Copyrighted [year] [name of copyright owner]" |
23 |
* |
24 |
* If you wish your version of this file to be governed by only the CDDL |
25 |
* or only the GPL Version 2, indicate your decision by adding |
26 |
* "[Contributor] elects to include this software in this distribution |
27 |
* under the [CDDL or GPL Version 2] license." If you do not indicate a |
28 |
* single choice of license, a recipient has the option to distribute |
29 |
* your version of this file under either the CDDL, the GPL Version 2 or |
30 |
* to extend the choice of license to its licensees as provided above. |
31 |
* However, if you add GPL Version 2 code and therefore, elected the GPL |
32 |
* Version 2 license, then the option applies only if the new code is |
33 |
* made subject to such option by the copyright holder. |
34 |
* |
35 |
* Contributor(s): |
36 |
* |
37 |
* Portions Copyrighted 2008 Sun Microsystems, Inc. |
38 |
*/ |
39 |
|
40 |
package org.openide.loaders; |
41 |
|
42 |
import java.io.BufferedReader; |
43 |
import java.io.BufferedWriter; |
44 |
import java.io.ByteArrayInputStream; |
45 |
import java.io.IOException; |
46 |
import java.io.InputStream; |
47 |
import java.io.InputStreamReader; |
48 |
import java.io.OutputStream; |
49 |
import java.io.OutputStreamWriter; |
50 |
import java.io.Reader; |
51 |
import java.io.Writer; |
52 |
import java.nio.ByteBuffer; |
53 |
import java.nio.CharBuffer; |
54 |
import java.nio.charset.Charset; |
55 |
import java.nio.charset.CharsetDecoder; |
56 |
import java.nio.charset.CharsetEncoder; |
57 |
import java.nio.charset.CoderResult; |
58 |
import java.util.Map; |
59 |
import org.netbeans.api.queries.FileEncodingQuery; |
60 |
import org.netbeans.junit.MockServices; |
61 |
import org.netbeans.junit.NbTestCase; |
62 |
import org.netbeans.modules.openide.loaders.DataObjectEncodingQueryImplementation; |
63 |
import org.netbeans.spi.queries.FileEncodingQueryImplementation; |
64 |
import org.openide.filesystems.FileObject; |
65 |
import org.openide.filesystems.FileUtil; |
66 |
import org.openide.util.Lookup; |
67 |
import org.openide.util.lookup.Lookups; |
68 |
|
69 |
/** |
70 |
* |
71 |
* @author Marian Petras |
72 |
*/ |
73 |
public class Bug138973Test extends NbTestCase { |
74 |
|
75 |
private static final String TESTING_TEXT = "This is a testing text."; |
76 |
private static final TestCharset TEST_CHARSET = new TestCharset(); |
77 |
private static final String EXT = ".txt"; |
78 |
private static final String TEMPLATE_NAME = "Bug138973TestTemplate"; |
79 |
private static final String TEMPLATE_NAME_EXT = TEMPLATE_NAME + EXT; |
80 |
private static final String TESTFILE_NAME = "testfile"; |
81 |
private static final String TESTFILE_NAME_EXT = TESTFILE_NAME + EXT; |
82 |
|
83 |
public Bug138973Test() { |
84 |
super("Bug138973Test"); |
85 |
} |
86 |
|
87 |
public void testBug() throws Exception { |
88 |
AddLoaderManuallyHid.addRemoveLoader(new SimpleLoader(), true); |
89 |
MockServices.setServices(SimpleTemplateHandler.class, |
90 |
DataObjectEncodingQueryImplementation.class); |
91 |
|
92 |
FileObject root = FileUtil.createMemoryFileSystem().getRoot(); |
93 |
FileObject templatesFolder = root.createFolder("templates"); |
94 |
assert templatesFolder != null; |
95 |
FileObject templateFile = FileUtil.createData(templatesFolder, |
96 |
TEMPLATE_NAME_EXT); |
97 |
byte[] templateBytes = TESTING_TEXT.getBytes("ISO-8859-1"); |
98 |
InputStream source = new ByteArrayInputStream(templateBytes); |
99 |
OutputStream target = templateFile.getOutputStream(); |
100 |
FileUtil.copy(source, target); |
101 |
target.close(); |
102 |
source.close(); |
103 |
assert templateFile.getSize() != 0L; |
104 |
templateFile.setAttribute("template", Boolean.TRUE); |
105 |
|
106 |
DataObject templateDataObj = DataObject.find(templateFile); |
107 |
DataObject newDataObj= templateDataObj.createFromTemplate( |
108 |
DataFolder.findFolder(root), |
109 |
TESTFILE_NAME); |
110 |
FileObject newFile = newDataObj.getPrimaryFile(); |
111 |
|
112 |
byte[] expectedBytes = new byte[TESTING_TEXT.length() * 4]; |
113 |
TEST_CHARSET.encode(TESTING_TEXT).get(expectedBytes); |
114 |
|
115 |
byte[] actualBytes = new byte[(int) (newFile.getSize())]; |
116 |
InputStream is = null; |
117 |
try { |
118 |
is = newFile.getInputStream(); |
119 |
newFile.getInputStream().read(actualBytes); |
120 |
} finally { |
121 |
if (is != null) { |
122 |
is.close(); |
123 |
} |
124 |
} |
125 |
assertEquals(expectedBytes, actualBytes); |
126 |
} |
127 |
|
128 |
private void assertEquals(byte[] expected, byte[] actual) throws Exception { |
129 |
if ((expected == null) && (actual == null)) { |
130 |
return; |
131 |
} |
132 |
if ((expected == null) || (actual == null)) { |
133 |
if (actual == null) { |
134 |
fail("actual is null"); |
135 |
} else{ |
136 |
fail("expected is null"); |
137 |
} |
138 |
} |
139 |
if (expected.length != actual.length) { |
140 |
fail("array lengths differ - expected: " |
141 |
+ expected.length + ", actual: " + actual.length); |
142 |
} |
143 |
for (int i = 0; i < actual.length; i++) { |
144 |
if (actual[i] != expected[i]) { |
145 |
String expectedStr = new String(expected, "ISO-8859-1"); |
146 |
String actualStr = new String(actual, "ISO-8859-1"); |
147 |
fail("expected: \"" + expectedStr |
148 |
+ "\", actual: \"" + actualStr + '"'); |
149 |
} |
150 |
} |
151 |
} |
152 |
|
153 |
public static final class SimpleTemplateHandler extends CreateFromTemplateHandler { |
154 |
@Override |
155 |
protected boolean accept(FileObject orig) { |
156 |
return true; |
157 |
} |
158 |
@Override |
159 |
protected FileObject createFromTemplate(FileObject template, |
160 |
FileObject targetFolder, |
161 |
String name, |
162 |
Map<String, Object> parameters) throws IOException { |
163 |
String nameUniq = FileUtil.findFreeFileName(targetFolder, name, template.getExt()); |
164 |
FileObject newFile = FileUtil.createData(targetFolder, nameUniq + '.' + template.getExt()); |
165 |
|
166 |
Charset templateEnc = FileEncodingQuery.getEncoding(template); |
167 |
Charset newFileEnc = FileEncodingQuery.getEncoding(newFile); |
168 |
|
169 |
InputStream is = template.getInputStream(); |
170 |
Reader reader = new BufferedReader(new InputStreamReader(is, templateEnc)); |
171 |
OutputStream os = newFile.getOutputStream(); |
172 |
Writer writer = new BufferedWriter(new OutputStreamWriter(os, newFileEnc)); |
173 |
int cInt; |
174 |
while ((cInt = reader.read()) != -1) { |
175 |
writer.write(cInt); |
176 |
} |
177 |
writer.close(); |
178 |
reader.close(); |
179 |
|
180 |
return newFile; |
181 |
} |
182 |
} |
183 |
|
184 |
public static final class SimpleLoader extends MultiFileLoader { |
185 |
public SimpleLoader() { |
186 |
super(SimpleObject.class.getName()); |
187 |
} |
188 |
protected String displayName() { |
189 |
return "SimpleLoader"; |
190 |
} |
191 |
protected FileObject findPrimaryFile(FileObject fo) { |
192 |
if (fo.getNameExt().equals(TEMPLATE_NAME_EXT)) { |
193 |
return fo; |
194 |
} |
195 |
if (fo.getNameExt().equals(TESTFILE_NAME_EXT)) { |
196 |
return fo; |
197 |
} |
198 |
return null; |
199 |
} |
200 |
protected MultiDataObject createMultiObject(FileObject primaryFile) |
201 |
throws DataObjectExistsException, |
202 |
IOException { |
203 |
return new SimpleObject(this, primaryFile, isTestingFile(primaryFile)); |
204 |
} |
205 |
protected MultiDataObject.Entry createPrimaryEntry(MultiDataObject obj, |
206 |
FileObject primaryFile) { |
207 |
return new FE(obj, primaryFile); |
208 |
} |
209 |
protected MultiDataObject.Entry createSecondaryEntry(MultiDataObject obj, |
210 |
FileObject secondaryFile) { |
211 |
return new FE(obj, secondaryFile); |
212 |
} |
213 |
private static boolean isTestingFile(FileObject fileObj) { |
214 |
return fileObj.getNameExt().equals(TESTFILE_NAME_EXT); |
215 |
} |
216 |
} |
217 |
|
218 |
private static final class FE extends FileEntry { |
219 |
public FE(MultiDataObject mo, FileObject fo) { |
220 |
super(mo, fo); |
221 |
} |
222 |
@Override |
223 |
public FileObject createFromTemplate(FileObject f, String name) throws IOException { |
224 |
fail("FileEntry.createFromTemplate() should not be called"); |
225 |
return null; |
226 |
} |
227 |
} |
228 |
|
229 |
public static final class SimpleObject extends MultiDataObject { |
230 |
private final Lookup lookup; |
231 |
public SimpleObject(SimpleLoader l, |
232 |
FileObject fo, |
233 |
boolean useSpecialEncoding) |
234 |
throws DataObjectExistsException { |
235 |
super(fo, l); |
236 |
lookup = useSpecialEncoding |
237 |
? Lookups.fixed(this, new TestEncoding()) |
238 |
: Lookups.singleton(this); |
239 |
} |
240 |
@Override |
241 |
public String getName() { |
242 |
return getPrimaryFile().getNameExt(); |
243 |
} |
244 |
@Override |
245 |
public Lookup getLookup() { |
246 |
return lookup; |
247 |
} |
248 |
} |
249 |
|
250 |
static final class TestEncoding extends FileEncodingQueryImplementation { |
251 |
@Override |
252 |
public Charset getEncoding(FileObject file) { |
253 |
return TEST_CHARSET; |
254 |
} |
255 |
} |
256 |
|
257 |
static final class TestCharset extends Charset { |
258 |
TestCharset() { |
259 |
super("test_charset", null); |
260 |
} |
261 |
public boolean contains(Charset charset) { |
262 |
return true; |
263 |
} |
264 |
public CharsetDecoder newDecoder() { |
265 |
return new TestCharsetDecoder(this); |
266 |
} |
267 |
public CharsetEncoder newEncoder() { |
268 |
return new TestCharsetEncoder(this); |
269 |
} |
270 |
} |
271 |
|
272 |
static final class TestCharsetDecoder extends CharsetDecoder { |
273 |
private static final String hexadecimalChars |
274 |
= "0123456789abcdef"; //NOI18N |
275 |
TestCharsetDecoder(Charset charset) { |
276 |
super(charset, 1.0f, 1.0f); |
277 |
} |
278 |
@Override |
279 |
protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { |
280 |
for (;;) { |
281 |
int value = 0; |
282 |
|
283 |
byte b; |
284 |
char bChar; |
285 |
int index; |
286 |
|
287 |
if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } |
288 |
b = in.get(); |
289 |
bChar = (char) (b >= 0 ? b : b + 256); |
290 |
index = hexadecimalChars.indexOf(bChar); |
291 |
if (index == -1) { return CoderResult.malformedForLength(1); } |
292 |
value = index; |
293 |
|
294 |
if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } |
295 |
b = in.get(); |
296 |
bChar = (char) (b >= 0 ? b : b + 256); |
297 |
index = hexadecimalChars.indexOf(bChar); |
298 |
if (index == -1) { return CoderResult.malformedForLength(2); } |
299 |
value = (value << 4) | index; |
300 |
|
301 |
if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } |
302 |
b = in.get(); |
303 |
bChar = (char) (b >= 0 ? b : b + 256); |
304 |
index = hexadecimalChars.indexOf(bChar); |
305 |
if (index == -1) { return CoderResult.malformedForLength(3); } |
306 |
value = (value << 4) | index; |
307 |
|
308 |
if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } |
309 |
b = in.get(); |
310 |
bChar = (char) (b >= 0 ? b : b + 256); |
311 |
index = hexadecimalChars.indexOf(bChar); |
312 |
if (index == -1) { return CoderResult.malformedForLength(4); } |
313 |
value = (value << 4) | index; |
314 |
|
315 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
316 |
out.put((char) value); |
317 |
} |
318 |
} |
319 |
} |
320 |
|
321 |
/** |
322 |
* Encodes each character as a series of four hexadecimal digits expressing |
323 |
* the character's Unicode value. |
324 |
*/ |
325 |
static final class TestCharsetEncoder extends CharsetEncoder { |
326 |
private static final byte[] hexadecimalChars |
327 |
= {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; |
328 |
TestCharsetEncoder(Charset charset) { |
329 |
super(charset, 4.0f, 4.0f, new byte[] {0x30, 0x30, 0x33, 0x66}); |
330 |
} |
331 |
@Override |
332 |
protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { |
333 |
for (;;) { |
334 |
if (!in.hasRemaining()) { |
335 |
return CoderResult.UNDERFLOW; |
336 |
} |
337 |
char c = in.get(); |
338 |
int cInt = (int) c; |
339 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
340 |
out.put(hexadecimalChars[(cInt >> 12) & 0x000f]); |
341 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
342 |
out.put(hexadecimalChars[(cInt >> 8) & 0x000f]); |
343 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
344 |
out.put(hexadecimalChars[(cInt >> 4) & 0x000f]); |
345 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
346 |
out.put(hexadecimalChars[ cInt & 0x000f]); |
347 |
} |
348 |
} |
349 |
|
350 |
} |
351 |
|
352 |
} |