diff --git a/openide.loaders/test/unit/src/org/openide/loaders/Bug138973Test.java b/openide.loaders/test/unit/src/org/openide/loaders/Bug138973Test.java new file mode 100644 --- /dev/null +++ b/openide.loaders/test/unit/src/org/openide/loaders/Bug138973Test.java @@ -0,0 +1,352 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.openide.loaders; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.Reader; +import java.io.Writer; +import java.nio.ByteBuffer; +import java.nio.CharBuffer; +import java.nio.charset.Charset; +import java.nio.charset.CharsetDecoder; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.CoderResult; +import java.util.Map; +import org.netbeans.api.queries.FileEncodingQuery; +import org.netbeans.junit.MockServices; +import org.netbeans.junit.NbTestCase; +import org.netbeans.modules.openide.loaders.DataObjectEncodingQueryImplementation; +import org.netbeans.spi.queries.FileEncodingQueryImplementation; +import org.openide.filesystems.FileObject; +import org.openide.filesystems.FileUtil; +import org.openide.util.Lookup; +import org.openide.util.lookup.Lookups; + +/** + * + * @author Marian Petras + */ +public class Bug138973Test extends NbTestCase { + + private static final String TESTING_TEXT = "This is a testing text."; + private static final TestCharset TEST_CHARSET = new TestCharset(); + private static final String EXT = ".txt"; + private static final String TEMPLATE_NAME = "Bug138973TestTemplate"; + private static final String TEMPLATE_NAME_EXT = TEMPLATE_NAME + EXT; + private static final String TESTFILE_NAME = "testfile"; + private static final String TESTFILE_NAME_EXT = TESTFILE_NAME + EXT; + + public Bug138973Test() { + super("Bug138973Test"); + } + + public void testBug() throws Exception { + AddLoaderManuallyHid.addRemoveLoader(new SimpleLoader(), true); + MockServices.setServices(SimpleTemplateHandler.class, + DataObjectEncodingQueryImplementation.class); + + FileObject root = FileUtil.createMemoryFileSystem().getRoot(); + FileObject templatesFolder = root.createFolder("templates"); + assert templatesFolder != null; + FileObject templateFile = FileUtil.createData(templatesFolder, + TEMPLATE_NAME_EXT); + byte[] templateBytes = TESTING_TEXT.getBytes("ISO-8859-1"); + InputStream source = new ByteArrayInputStream(templateBytes); + OutputStream target = templateFile.getOutputStream(); + FileUtil.copy(source, target); + target.close(); + source.close(); + assert templateFile.getSize() != 0L; + templateFile.setAttribute("template", Boolean.TRUE); + + DataObject templateDataObj = DataObject.find(templateFile); + DataObject newDataObj= templateDataObj.createFromTemplate( + DataFolder.findFolder(root), + TESTFILE_NAME); + FileObject newFile = newDataObj.getPrimaryFile(); + + byte[] expectedBytes = new byte[TESTING_TEXT.length() * 4]; + TEST_CHARSET.encode(TESTING_TEXT).get(expectedBytes); + + byte[] actualBytes = new byte[(int) (newFile.getSize())]; + InputStream is = null; + try { + is = newFile.getInputStream(); + newFile.getInputStream().read(actualBytes); + } finally { + if (is != null) { + is.close(); + } + } + assertEquals(expectedBytes, actualBytes); + } + + private void assertEquals(byte[] expected, byte[] actual) throws Exception { + if ((expected == null) && (actual == null)) { + return; + } + if ((expected == null) || (actual == null)) { + if (actual == null) { + fail("actual is null"); + } else{ + fail("expected is null"); + } + } + if (expected.length != actual.length) { + fail("array lengths differ - expected: " + + expected.length + ", actual: " + actual.length); + } + for (int i = 0; i < actual.length; i++) { + if (actual[i] != expected[i]) { + String expectedStr = new String(expected, "ISO-8859-1"); + String actualStr = new String(actual, "ISO-8859-1"); + fail("expected: \"" + expectedStr + + "\", actual: \"" + actualStr + '"'); + } + } + } + + public static final class SimpleTemplateHandler extends CreateFromTemplateHandler { + @Override + protected boolean accept(FileObject orig) { + return true; + } + @Override + protected FileObject createFromTemplate(FileObject template, + FileObject targetFolder, + String name, + Map parameters) throws IOException { + String nameUniq = FileUtil.findFreeFileName(targetFolder, name, template.getExt()); + FileObject newFile = FileUtil.createData(targetFolder, nameUniq + '.' + template.getExt()); + + Charset templateEnc = FileEncodingQuery.getEncoding(template); + Charset newFileEnc = FileEncodingQuery.getEncoding(newFile); + + InputStream is = template.getInputStream(); + Reader reader = new BufferedReader(new InputStreamReader(is, templateEnc)); + OutputStream os = newFile.getOutputStream(); + Writer writer = new BufferedWriter(new OutputStreamWriter(os, newFileEnc)); + int cInt; + while ((cInt = reader.read()) != -1) { + writer.write(cInt); + } + writer.close(); + reader.close(); + + return newFile; + } + } + + public static final class SimpleLoader extends MultiFileLoader { + public SimpleLoader() { + super(SimpleObject.class.getName()); + } + protected String displayName() { + return "SimpleLoader"; + } + protected FileObject findPrimaryFile(FileObject fo) { + if (fo.getNameExt().equals(TEMPLATE_NAME_EXT)) { + return fo; + } + if (fo.getNameExt().equals(TESTFILE_NAME_EXT)) { + return fo; + } + return null; + } + protected MultiDataObject createMultiObject(FileObject primaryFile) + throws DataObjectExistsException, + IOException { + return new SimpleObject(this, primaryFile, isTestingFile(primaryFile)); + } + protected MultiDataObject.Entry createPrimaryEntry(MultiDataObject obj, + FileObject primaryFile) { + return new FE(obj, primaryFile); + } + protected MultiDataObject.Entry createSecondaryEntry(MultiDataObject obj, + FileObject secondaryFile) { + return new FE(obj, secondaryFile); + } + private static boolean isTestingFile(FileObject fileObj) { + return fileObj.getNameExt().equals(TESTFILE_NAME_EXT); + } + } + + private static final class FE extends FileEntry { + public FE(MultiDataObject mo, FileObject fo) { + super(mo, fo); + } + @Override + public FileObject createFromTemplate(FileObject f, String name) throws IOException { + fail("FileEntry.createFromTemplate() should not be called"); + return null; + } + } + + public static final class SimpleObject extends MultiDataObject { + private final Lookup lookup; + public SimpleObject(SimpleLoader l, + FileObject fo, + boolean useSpecialEncoding) + throws DataObjectExistsException { + super(fo, l); + lookup = useSpecialEncoding + ? Lookups.fixed(this, new TestEncoding()) + : Lookups.singleton(this); + } + @Override + public String getName() { + return getPrimaryFile().getNameExt(); + } + @Override + public Lookup getLookup() { + return lookup; + } + } + + static final class TestEncoding extends FileEncodingQueryImplementation { + @Override + public Charset getEncoding(FileObject file) { + return TEST_CHARSET; + } + } + + static final class TestCharset extends Charset { + TestCharset() { + super("test_charset", null); + } + public boolean contains(Charset charset) { + return true; + } + public CharsetDecoder newDecoder() { + return new TestCharsetDecoder(this); + } + public CharsetEncoder newEncoder() { + return new TestCharsetEncoder(this); + } + } + + static final class TestCharsetDecoder extends CharsetDecoder { + private static final String hexadecimalChars + = "0123456789abcdef"; //NOI18N + TestCharsetDecoder(Charset charset) { + super(charset, 1.0f, 1.0f); + } + @Override + protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) { + for (;;) { + int value = 0; + + byte b; + char bChar; + int index; + + if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } + b = in.get(); + bChar = (char) (b >= 0 ? b : b + 256); + index = hexadecimalChars.indexOf(bChar); + if (index == -1) { return CoderResult.malformedForLength(1); } + value = index; + + if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } + b = in.get(); + bChar = (char) (b >= 0 ? b : b + 256); + index = hexadecimalChars.indexOf(bChar); + if (index == -1) { return CoderResult.malformedForLength(2); } + value = (value << 4) | index; + + if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } + b = in.get(); + bChar = (char) (b >= 0 ? b : b + 256); + index = hexadecimalChars.indexOf(bChar); + if (index == -1) { return CoderResult.malformedForLength(3); } + value = (value << 4) | index; + + if (!in.hasRemaining()) { return CoderResult.UNDERFLOW; } + b = in.get(); + bChar = (char) (b >= 0 ? b : b + 256); + index = hexadecimalChars.indexOf(bChar); + if (index == -1) { return CoderResult.malformedForLength(4); } + value = (value << 4) | index; + + if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } + out.put((char) value); + } + } + } + + /** + * Encodes each character as a series of four hexadecimal digits expressing + * the character's Unicode value. + */ + static final class TestCharsetEncoder extends CharsetEncoder { + private static final byte[] hexadecimalChars + = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; + TestCharsetEncoder(Charset charset) { + super(charset, 4.0f, 4.0f, new byte[] {0x30, 0x30, 0x33, 0x66}); + } + @Override + protected CoderResult encodeLoop(CharBuffer in, ByteBuffer out) { + for (;;) { + if (!in.hasRemaining()) { + return CoderResult.UNDERFLOW; + } + char c = in.get(); + int cInt = (int) c; + if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } + out.put(hexadecimalChars[(cInt >> 12) & 0x000f]); + if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } + out.put(hexadecimalChars[(cInt >> 8) & 0x000f]); + if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } + out.put(hexadecimalChars[(cInt >> 4) & 0x000f]); + if (!out.hasRemaining()) { return CoderResult.OVERFLOW; } + out.put(hexadecimalChars[ cInt & 0x000f]); + } + } + + } + +} \ No newline at end of file