Added
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.netbeans.modules.templates; |
41 |
|
42 |
import java.util.Enumeration; |
43 |
import org.openide.loaders.*; |
44 |
import java.io.BufferedReader; |
45 |
import java.io.BufferedWriter; |
46 |
import java.io.ByteArrayInputStream; |
47 |
import java.io.IOException; |
48 |
import java.io.InputStream; |
49 |
import java.io.InputStreamReader; |
50 |
import java.io.OutputStream; |
51 |
import java.io.OutputStreamWriter; |
52 |
import java.io.Reader; |
53 |
import java.io.Writer; |
54 |
import java.nio.ByteBuffer; |
55 |
import java.nio.CharBuffer; |
56 |
import java.nio.charset.Charset; |
57 |
import java.nio.charset.CharsetDecoder; |
58 |
import java.nio.charset.CharsetEncoder; |
59 |
import java.nio.charset.CoderResult; |
60 |
import java.util.Map; |
61 |
import org.netbeans.api.queries.FileEncodingQuery; |
62 |
import org.netbeans.junit.MockServices; |
63 |
import org.netbeans.junit.NbTestCase; |
64 |
import org.netbeans.modules.openide.loaders.DataObjectEncodingQueryImplementation; |
65 |
import org.netbeans.spi.queries.FileEncodingQueryImplementation; |
66 |
import org.openide.filesystems.FileObject; |
67 |
import org.openide.filesystems.FileUtil; |
68 |
import org.openide.util.Enumerations; |
69 |
import org.openide.util.Lookup; |
70 |
import org.openide.util.lookup.Lookups; |
71 |
|
72 |
/** |
73 |
* |
74 |
* @author Marian Petras |
75 |
*/ |
76 |
public class Bug138973Test extends NbTestCase { |
77 |
|
78 |
private static final String TESTING_TEXT = "This is a testing text."; |
79 |
private static final TestCharset TEST_CHARSET = new TestCharset(); |
80 |
private static final String EXT = ".txt"; |
81 |
private static final String TEMPLATE_NAME = "Bug138973TestTemplate"; |
82 |
private static final String TEMPLATE_NAME_EXT = TEMPLATE_NAME + EXT; |
83 |
private static final String TESTFILE_NAME = "testfile"; |
84 |
private static final String TESTFILE_NAME_EXT = TESTFILE_NAME + EXT; |
85 |
|
86 |
public Bug138973Test() { |
87 |
super("Bug138973Test"); |
88 |
} |
89 |
|
90 |
public void testBug() throws Exception { |
91 |
MockServices.setServices(Pool.class, DataObjectEncodingQueryImplementation.class); |
92 |
|
93 |
FileObject root = FileUtil.createMemoryFileSystem().getRoot(); |
94 |
FileObject templatesFolder = root.createFolder("templates"); |
95 |
assert templatesFolder != null; |
96 |
FileObject templateFile = FileUtil.createData(templatesFolder, |
97 |
TEMPLATE_NAME_EXT); |
98 |
templateFile.setAttribute ("template", Boolean.TRUE); |
99 |
templateFile.setAttribute("javax.script.ScriptEngine", "freemarker"); |
100 |
byte[] templateBytes = TESTING_TEXT.getBytes("ISO-8859-1"); |
101 |
InputStream source = new ByteArrayInputStream(templateBytes); |
102 |
OutputStream target = templateFile.getOutputStream(); |
103 |
FileUtil.copy(source, target); |
104 |
target.close(); |
105 |
source.close(); |
106 |
assert templateFile.getSize() != 0L; |
107 |
templateFile.setAttribute("template", Boolean.TRUE); |
108 |
|
109 |
DataObject templateDataObj = DataObject.find(templateFile); |
110 |
DataObject newDataObj= templateDataObj.createFromTemplate( |
111 |
DataFolder.findFolder(root), |
112 |
TESTFILE_NAME); |
113 |
FileObject newFile = newDataObj.getPrimaryFile(); |
114 |
|
115 |
byte[] expectedBytes = new byte[TESTING_TEXT.length() * 4]; |
116 |
TEST_CHARSET.encode(TESTING_TEXT).get(expectedBytes); |
117 |
|
118 |
byte[] actualBytes = new byte[(int) (newFile.getSize())]; |
119 |
InputStream is = null; |
120 |
try { |
121 |
is = newFile.getInputStream(); |
122 |
newFile.getInputStream().read(actualBytes); |
123 |
} finally { |
124 |
if (is != null) { |
125 |
is.close(); |
126 |
} |
127 |
} |
128 |
assertEquals(expectedBytes, actualBytes); |
129 |
} |
130 |
|
131 |
private void assertEquals(byte[] expected, byte[] actual) throws Exception { |
132 |
if ((expected == null) && (actual == null)) { |
133 |
return; |
134 |
} |
135 |
if ((expected == null) || (actual == null)) { |
136 |
if (actual == null) { |
137 |
fail("actual is null"); |
138 |
} else{ |
139 |
fail("expected is null"); |
140 |
} |
141 |
} |
142 |
if (expected.length != actual.length) { |
143 |
fail("array lengths differ - expected: " |
144 |
+ expected.length + ", actual: " + actual.length); |
145 |
} |
146 |
for (int i = 0; i < actual.length; i++) { |
147 |
if (actual[i] != expected[i]) { |
148 |
String expectedStr = new String(expected, "ISO-8859-1"); |
149 |
String actualStr = new String(actual, "ISO-8859-1"); |
150 |
fail("expected: \"" + expectedStr |
151 |
+ "\", actual: \"" + actualStr + '"'); |
152 |
} |
153 |
} |
154 |
} |
155 |
|
156 |
public static final class SimpleTemplateHandler extends CreateFromTemplateHandler { |
157 |
@Override |
158 |
protected boolean accept(FileObject orig) { |
159 |
return true; |
160 |
} |
161 |
@Override |
162 |
protected FileObject createFromTemplate(FileObject template, |
163 |
FileObject targetFolder, |
164 |
String name, |
165 |
Map<String, Object> parameters) throws IOException { |
166 |
String nameUniq = FileUtil.findFreeFileName(targetFolder, name, template.getExt()); |
167 |
FileObject newFile = FileUtil.createData(targetFolder, nameUniq + '.' + template.getExt()); |
168 |
|
169 |
Charset templateEnc = FileEncodingQuery.getEncoding(template); |
170 |
Charset newFileEnc = FileEncodingQuery.getEncoding(newFile); |
171 |
|
172 |
InputStream is = template.getInputStream(); |
173 |
Reader reader = new BufferedReader(new InputStreamReader(is, templateEnc)); |
174 |
OutputStream os = newFile.getOutputStream(); |
175 |
Writer writer = new BufferedWriter(new OutputStreamWriter(os, newFileEnc)); |
176 |
int cInt; |
177 |
while ((cInt = reader.read()) != -1) { |
178 |
writer.write(cInt); |
179 |
} |
180 |
writer.close(); |
181 |
reader.close(); |
182 |
|
183 |
return newFile; |
184 |
} |
185 |
} |
186 |
|
187 |
public static final class SimpleLoader extends MultiFileLoader { |
188 |
public SimpleLoader() { |
189 |
super(SimpleObject.class.getName()); |
190 |
} |
191 |
protected String displayName() { |
192 |
return "SimpleLoader"; |
193 |
} |
194 |
protected FileObject findPrimaryFile(FileObject fo) { |
195 |
if (fo.getNameExt().equals(TEMPLATE_NAME_EXT)) { |
196 |
return fo; |
197 |
} |
198 |
if (fo.getNameExt().equals(TESTFILE_NAME_EXT)) { |
199 |
return fo; |
200 |
} |
201 |
return null; |
202 |
} |
203 |
protected MultiDataObject createMultiObject(FileObject primaryFile) |
204 |
throws DataObjectExistsException, |
205 |
IOException { |
206 |
return new SimpleObject(this, primaryFile, isTestingFile(primaryFile)); |
207 |
} |
208 |
protected MultiDataObject.Entry createPrimaryEntry(MultiDataObject obj, |
209 |
FileObject primaryFile) { |
210 |
return new FE(obj, primaryFile); |
211 |
} |
212 |
protected MultiDataObject.Entry createSecondaryEntry(MultiDataObject obj, |
213 |
FileObject secondaryFile) { |
214 |
return new FE(obj, secondaryFile); |
215 |
} |
216 |
private static boolean isTestingFile(FileObject fileObj) { |
217 |
return fileObj.getNameExt().equals(TESTFILE_NAME_EXT); |
218 |
} |
219 |
} |
220 |
|
221 |
private static final class FE extends FileEntry { |
222 |
public FE(MultiDataObject mo, FileObject fo) { |
223 |
super(mo, fo); |
224 |
} |
225 |
@Override |
226 |
public FileObject createFromTemplate(FileObject f, String name) throws IOException { |
227 |
fail("FileEntry.createFromTemplate() should not be called"); |
228 |
return null; |
229 |
} |
230 |
} |
231 |
|
232 |
public static final class SimpleObject extends MultiDataObject { |
233 |
private final Lookup lookup; |
234 |
public SimpleObject(SimpleLoader l, |
235 |
FileObject fo, |
236 |
boolean useSpecialEncoding) |
237 |
throws DataObjectExistsException { |
238 |
super(fo, l); |
239 |
lookup = useSpecialEncoding |
240 |
? Lookups.fixed(this, new TestEncoding()) |
241 |
: Lookups.singleton(this); |
242 |
} |
243 |
@Override |
244 |
public String getName() { |
245 |
return getPrimaryFile().getNameExt(); |
246 |
} |
247 |
@Override |
248 |
public Lookup getLookup() { |
249 |
return lookup; |
250 |
} |
251 |
} |
252 |
|
253 |
static final class TestEncoding extends FileEncodingQueryImplementation { |
254 |
@Override |
255 |
public Charset getEncoding(FileObject file) { |
256 |
return TEST_CHARSET; |
257 |
} |
258 |
} |
259 |
|
260 |
static final class TestCharset extends Charset { |
261 |
TestCharset() { |
262 |
super("test_charset", null); |
263 |
} |
264 |
public boolean contains(Charset charset) { |
265 |
return true; |
266 |
} |
267 |
public CharsetDecoder newDecoder() { |
268 |
return new TestCharsetDecoder(this); |
269 |
} |
270 |
public CharsetEncoder newEncoder() { |
271 |
return new TestCharsetEncoder(this); |
272 |
} |
273 |
} |
274 |
|
275 |
static final class TestCharsetDecoder extends CharsetDecoder { |
276 |
private static final String hexadecimalChars |
277 |
= "0123456789abcdef"; //NOI18N |
278 |
TestCharsetDecoder(Charset charset) { |
279 |
super(charset, 1.0f, 1.0f); |
280 |
} |
281 |
@Override |
282 |
protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { |
283 |
for (;;) { |
284 |
int value = 0; |
285 |
|
286 |
byte b; |
287 |
char bChar; |
288 |
int index; |
289 |
|
290 |
if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } |
291 |
b = in.get(); |
292 |
bChar = (char) (b >= 0 ? b : b + 256); |
293 |
index = hexadecimalChars.indexOf(bChar); |
294 |
if (index == -1) { return CoderResult.malformedForLength(1); } |
295 |
value = index; |
296 |
|
297 |
if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } |
298 |
b = in.get(); |
299 |
bChar = (char) (b >= 0 ? b : b + 256); |
300 |
index = hexadecimalChars.indexOf(bChar); |
301 |
if (index == -1) { return CoderResult.malformedForLength(2); } |
302 |
value = (value << 4) | index; |
303 |
|
304 |
if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } |
305 |
b = in.get(); |
306 |
bChar = (char) (b >= 0 ? b : b + 256); |
307 |
index = hexadecimalChars.indexOf(bChar); |
308 |
if (index == -1) { return CoderResult.malformedForLength(3); } |
309 |
value = (value << 4) | index; |
310 |
|
311 |
if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } |
312 |
b = in.get(); |
313 |
bChar = (char) (b >= 0 ? b : b + 256); |
314 |
index = hexadecimalChars.indexOf(bChar); |
315 |
if (index == -1) { return CoderResult.malformedForLength(4); } |
316 |
value = (value << 4) | index; |
317 |
|
318 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
319 |
out.put((char) value); |
320 |
} |
321 |
} |
322 |
} |
323 |
|
324 |
/** |
325 |
* Encodes each character as a series of four hexadecimal digits expressing |
326 |
* the character's Unicode value. |
327 |
*/ |
328 |
static final class TestCharsetEncoder extends CharsetEncoder { |
329 |
private static final byte[] hexadecimalChars |
330 |
= {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; |
331 |
TestCharsetEncoder(Charset charset) { |
332 |
super(charset, 4.0f, 4.0f, new byte[] {0x30, 0x30, 0x33, 0x66}); |
333 |
} |
334 |
@Override |
335 |
protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { |
336 |
for (;;) { |
337 |
if (!in.hasRemaining()) { |
338 |
return CoderResult.UNDERFLOW; |
339 |
} |
340 |
char c = in.get(); |
341 |
int cInt = (int) c; |
342 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
343 |
out.put(hexadecimalChars[(cInt >> 12) & 0x000f]); |
344 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
345 |
out.put(hexadecimalChars[(cInt >> 8) & 0x000f]); |
346 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
347 |
out.put(hexadecimalChars[(cInt >> 4) & 0x000f]); |
348 |
if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } |
349 |
out.put(hexadecimalChars[ cInt & 0x000f]); |
350 |
} |
351 |
} |
352 |
|
353 |
} |
354 |
|
355 |
public static final class Pool extends DataLoaderPool { |
356 |
@Override |
357 |
protected Enumeration<? extends DataLoader> loaders() { |
358 |
return Enumerations.singleton(SimpleLoader.getLoader(SimpleLoader.class)); |
359 |
} |
360 |
|
361 |
} |
362 |
} |