diff -r bd8aef593115 openide.filesystems/apichanges.xml --- a/openide.filesystems/apichanges.xml Thu Jan 29 17:56:55 2009 +0100 +++ b/openide.filesystems/apichanges.xml Wed Feb 04 12:50:07 2009 +0100 @@ -46,6 +46,27 @@ Filesystems API + + + Read files with asText(), asBytes() and asLines() + + + + + +

+ Added + asBytes(), + + asText(encoding), + and + asLines(encoding) methods into + FileObject + to simplify reading of its content. +

+
+ +
Possibility to add FileChangeListeners on File (even not existing) diff -r bd8aef593115 openide.filesystems/nbproject/project.properties --- a/openide.filesystems/nbproject/project.properties Thu Jan 29 17:56:55 2009 +0100 +++ b/openide.filesystems/nbproject/project.properties Wed Feb 04 12:50:07 2009 +0100 @@ -44,4 +44,4 @@ javadoc.main.page=org/openide/filesystems/doc-files/api.html javadoc.arch=${basedir}/arch.xml javadoc.apichanges=${basedir}/apichanges.xml -spec.version.base=7.20.0 +spec.version.base=7.21.0 diff -r bd8aef593115 openide.filesystems/src/org/openide/filesystems/FileObject.java --- a/openide.filesystems/src/org/openide/filesystems/FileObject.java Thu Jan 29 17:56:55 2009 +0100 +++ b/openide.filesystems/src/org/openide/filesystems/FileObject.java Wed Feb 04 12:50:07 2009 +0100 @@ -49,13 +49,17 @@ import java.io.OutputStream; import java.io.Serializable; import java.net.URL; +import java.nio.charset.Charset; +import java.util.AbstractSequentialList; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.ListIterator; import java.util.StringTokenizer; import org.openide.util.Enumerations; import org.openide.util.NbBundle; @@ -161,6 +165,7 @@ * * @return some representation of this file object */ + @Override public String toString() { String cname = getClass().getName(); String cnameShort = cname.substring(cname.lastIndexOf('.') + 1); @@ -502,6 +507,121 @@ * rather than a regular file or is invalid */ public abstract InputStream getInputStream() throws FileNotFoundException; + + /** Reads the full content of the file object and returns it as array of + * bytes. + * @return array of bytes + * @exception IOException in case the content cannot be fully read + * @since 7.21 + */ + public byte[] asBytes() throws IOException { + long len = getSize(); + if (len > Integer.MAX_VALUE) { + throw new IOException("Too big file " + getPath()); // NOI18N + } + InputStream is = getInputStream(); + try { + byte[] arr = new byte[(int)len]; + int pos = 0; + while (pos < arr.length) { + int read = is.read(arr, pos, arr.length - pos); + if (read == -1) { + break; + } + pos += read; + } + if (pos != arr.length) { + throw new IOException("Just " + pos + " bytes read from " + getPath()); // NOI18N + } + return arr; + } finally { + is.close(); + } + } + + /** Reads the full content of the file object and returns it as string. + * @param encoding the encoding to use + * @return string representing the content of the file + * @exception IOException in case the content cannot be fully read + * @since 7.21 + */ + public String asText(String encoding) throws IOException { + return new String(asBytes(), encoding); + } + + /** Reads the full content of the file object and returns it as string. + * This is similar to calling {@link #asText(java.lang.String)} with + * default system encoding. + * + * @return string representing the content of the file + * @exception IOException in case the content cannot be fully read + * @since 7.21 + */ + public String asText() throws IOException { + return asText(Charset.defaultCharset().name()); + } + + /** Reads the full content of the file line by line with default + * system encoding. Typical usage is + * in for loops: + *
+     * for (String line : fo.asLines()) {
+     *   // do something
+     * }
+     * 
+ *

+ * The list is optimized for iterating line by line, other operations, + * like accessing all the lines or counting the number of its lines may + * be suboptimal. + * + * @return list of strings representing the content of the file + * @exception IOException in case the content cannot be fully read + * @since 7.21 + */ + public List asLines() throws IOException { + return asLines(Charset.defaultCharset().name()); + } + + /** Reads the full content of the file line by line. Typical usage is + * in for loops: + *

+     * for (String line : fo.asLines("UTF-8")) {
+     *   // do something
+     * }
+     * 
+ *

+ * The list is optimized for iterating line by line, other operations, + * like accessing all the lines or counting the number of its lines may + * be suboptimal. + * + * @param encoding the encoding to use + * @return list of strings representing the content of the file + * @exception IOException in case the content cannot be fully read + * @since 7.21 + */ + public List asLines(final String encoding) throws IOException { + return new AbstractSequentialList() { + final FileObjectLineIterator ready = new FileObjectLineIterator(FileObject.this, encoding); + + public synchronized ListIterator listIterator(int position) { + FileObjectLineIterator ret = ready.cloneIterator(); + while (position-- > 0) { + ret.next(); + } + return ret; + } + + public synchronized int size() { + int cnt = 0; + Iterator it = listIterator(); + while (it.hasNext()) { + it.next(); + cnt++; + } + return cnt; + } + }; + } /** Get output stream. * @param lock the lock that belongs to this file (obtained by a call to @@ -951,14 +1071,14 @@ } if (fs != null && fsList != null) { for (FileChangeListener fcl : fsList) { - fs.getFCLSupport().dispatchEvent(fcl, fe, op); + FCLSupport.dispatchEvent(fcl, fe, op); } } if (rep != null && repList != null) { for (FileChangeListener fcl : repList) { - rep.getFCLSupport().dispatchEvent(fcl, fe, op); + FCLSupport.dispatchEvent(fcl, fe, op); } } } diff -r bd8aef593115 openide.filesystems/src/org/openide/filesystems/FileObjectLineIterator.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/openide.filesystems/src/org/openide/filesystems/FileObjectLineIterator.java Wed Feb 04 12:50:07 2009 +0100 @@ -0,0 +1,167 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 1997-2007 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]" + * + * Contributor(s): + * + * The Original Software is NetBeans. The Initial Developer of the Original + * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun + * Microsystems, Inc. All Rights Reserved. + * + * 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. + */ +package org.openide.filesystems; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UnsupportedEncodingException; +import java.util.ListIterator; +import java.util.NoSuchElementException; +import org.openide.util.Exceptions; + +/** Iterator for list of lines. + * @author Jaroslav Tulach + */ +final class FileObjectLineIterator implements ListIterator { + private final FileObject fo; + private final String encoding; + private byte[] buffer; + private BufferedReader reader; + private String line; + private int index; + + public FileObjectLineIterator(FileObject fo, String encoding) throws IOException { + this.fo = fo; + this.encoding = encoding; + initReader(); + } + + private FileObjectLineIterator(FileObjectLineIterator orig) { + this.fo = orig.fo; + this.encoding = orig.encoding; + this.buffer = orig.buffer; + try { + initReader(); + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + + public synchronized boolean hasNext() { + if (line == null) { + try { + line = reader.readLine(); + if (line == null) { + reader.close(); + } + index++; + } catch (IOException ex) { + Exceptions.printStackTrace(ex); + } + } + return line != null; + } + + public synchronized String next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + String l = line; + line = null; + return l; + } + + public void remove() { + throw new UnsupportedOperationException(); + } + + public synchronized boolean hasPrevious() { + return index > 0; + } + + public synchronized String previous() { + if (index == 0) { + throw new NoSuchElementException(); + } + try { + int pos = index - 1; + initReader(); + String last = null; + while (index <= pos) { + last = next(); + } + index--; + return last; + } catch (IOException ex) { + throw (NoSuchElementException)new NoSuchElementException().initCause(ex); + } + } + + public synchronized int nextIndex() { + return index; + } + + public synchronized int previousIndex() { + return index - 1; + } + + public void set(String e) { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void add(String e) { + throw new UnsupportedOperationException("Not supported yet."); + } + + private void initReader() throws UnsupportedEncodingException, IOException { + InputStream is; + if (fo.getSize() < 64 * 1024) { + if (buffer == null) { + buffer = fo.asBytes(); + } + is = new ByteArrayInputStream(buffer); + } else { + is = fo.getInputStream(); + } + this.reader = new BufferedReader(new InputStreamReader(is, encoding)); + this.index = 0; + while (this.index < index) { + next(); + } + } + + final FileObjectLineIterator cloneIterator() { + return new FileObjectLineIterator(this); + } +} diff -r bd8aef593115 openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java --- a/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java Thu Jan 29 17:56:55 2009 +0100 +++ b/openide.filesystems/test/unit/src/org/openide/filesystems/FileObjectTestHid.java Wed Feb 04 12:50:07 2009 +0100 @@ -82,6 +82,7 @@ super(testName); } + @Override protected void setUp() throws java.lang.Exception { MockServices.setServices(); @@ -92,6 +93,7 @@ root = fs.findResource(getResourcePrefix()); } + @Override protected void tearDown() throws Exception { super.tearDown(); fs = this.testedFS = null; @@ -118,17 +120,20 @@ class L extends FileChangeAdapter { public int cnt; + @Override public void fileDataCreated(FileEvent fe) { cnt++; } } final FileChangeListener noFileDataCreatedListener = new FileChangeAdapter(){ + @Override public void fileDataCreated(FileEvent fe) { fail(); } }; final FileChangeListener listener1 = new FileChangeAdapter(){ + @Override public void fileDataCreated(FileEvent fe) { try { fold.getFileSystem().removeFileChangeListener(noFileDataCreatedListener); @@ -172,11 +177,13 @@ return; } final FileChangeListener noFileDataCreatedListener = new FileChangeAdapter(){ + @Override public void fileDataCreated(FileEvent fe) { fail(); } }; final FileChangeListener listener1 = new FileChangeAdapter(){ + @Override public void fileDataCreated(FileEvent fe) { fold.removeFileChangeListener(noFileDataCreatedListener); fold.addFileChangeListener(noFileDataCreatedListener); @@ -1543,6 +1550,7 @@ public static void implOfTestGetFileObjectForSubversion(final FileObject folder, final String childName) throws InterruptedException { final List l = new ArrayList(); folder.addFileChangeListener(new FileChangeAdapter(){ + @Override public void fileFolderCreated(FileEvent fe) { l.add(fe.getFile()); synchronized(l) { @@ -1812,6 +1820,120 @@ fsAssert ("After delete should be invalid",!fo1.isValid()); } + public void testAsBytesAndString() throws java.io.FileNotFoundException, IOException { + checkSetUp(); + + FileObject fo1 = getTestFile1 (root); + try { + OutputStream os = fo1.getOutputStream(); + String txt = "Ahoj\nJak\nSe\nMas"; + os.write(txt.getBytes("UTF-8")); + os.close(); + byte[] arr = fo1.asBytes(); + assertNotNull("Arrays is read", arr); + assertEquals("Right length bytes", txt.length(), arr.length); + assertEquals(txt, new String(arr, "UTF-8")); + assertEquals(txt, fo1.asText("UTF-8")); + + ArrayList all = new ArrayList(); + List lines = fo1.asLines("UTF-8"); + for (String l : lines) { + // it is possible to rewrite the content, if the file is small + fo1.getOutputStream().close(); + all.add(l); + } + assertEquals("Four lines: " + txt, 4, all.size()); + assertEquals("Computed remain the same as read", all, lines); + + ListIterator it = lines.listIterator(all.size()); + for (int i = all.size(); i > 0; ) { + i--; + assertEquals("Right index", i, it.previousIndex()); + assertEquals("Ith " + i, all.get(i), it.previous()); + } + } catch (IOException iex) { + fsAssert ("Expected that FS provides InputStream ",fo1.getSize () == 0); + } + FileLock lock = null; + + try { + lock = fo1.lock (); + fo1.delete(lock); + } catch (IOException iex) { + fsAssert("FileObject shopuld be allowd to be modified.", + fs.isReadOnly() || fo1.isReadOnly()) ; + return; + } finally { + if (lock != null) lock.releaseLock(); + } + fsAssert ("After delete should be invalid",!fo1.isValid()); + } + public void testBigFileAndAsString() throws java.io.FileNotFoundException, IOException { + checkSetUp(); + + FileObject fo1 = getTestFile1 (root); + try { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 1000; i++) { + sb.append("Hi how are you, boy! "); + } + sb.append("\n"); + + OutputStream os = fo1.getOutputStream(); + for (int i = 0; i < 10; i++) { + os.write(sb.toString().getBytes("UTF-8")); + } + os.close(); + if (64 * 1024 > fo1.getSize()) { + fail("We need to generate big file: " + fo1.getSize()); + } + + OutputStream tmp = null; + FileLock lock = null; + int acquire = 3; + List lines = fo1.asLines("UTF-8"); + LinkedList reverse = new LinkedList(); + for (int i = lines.size() - 1; i >= 0; i--) { + reverse.add(0, lines.get(i)); + } + + ArrayList all = new ArrayList(); + for (String l : fo1.asLines("UTF-8")) { + int cnt = all.size(); + if (cnt % 100 == 0 && acquire-- > 0) { + try { + lock = fo1.lock(); + tmp = fo1.getOutputStream(lock); + tmp.write(0); + fail("The input stream is open for big files. cnt = " + cnt); + } catch (IOException ex) { + // OK + } finally { + lock.releaseLock(); + } + } + all.add(l); + } + if (tmp != null) { + tmp.close(); + } + assertEquals("Some lines: ", 10, all.size()); + assertEquals("Generated same as read", all, fo1.asLines()); + assertEquals("Normal and reverse are same", all, reverse); + } catch (IOException iex) { + fsAssert ("Expected that FS provides InputStream ",fo1.getSize () == 0); + } + + try { + FileLock lock = fo1.lock (); + fo1.delete(lock); + } catch (IOException iex) { + fsAssert("FileObject shopuld be allowd to be modified.", + fs.isReadOnly() || fo1.isReadOnly()) ; + return; + } + fsAssert ("After delete should be invalid",!fo1.isValid()); + } /** Test of getOutputStream() method, of class org.openide.filesystems.FileObject. */ public void testGetOutputStream() throws java.io.IOException {