source: branches/0.16.x/abcl/src/org/armedbear/lisp/FileStream.java

Last change on this file was 11754, checked in by vvoutilainen, 16 years ago

Convert using ClassCastException? to checking instanceof.
Performance tests show this approach to be faster.
Patch by Douglas R. Miles. I modified the patch to
remove tabs, so indentation may be slightly off in places.
That's something that we need to handle separately, abcl
doesn't have a clear indentation policy.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.1 KB
Line 
1/*
2 * FileStream.java
3 *
4 * Copyright (C) 2004-2006 Peter Graves
5 * Copyright (C) 2008 Hideo at Yokohama
6 * $Id: FileStream.java 11754 2009-04-12 10:53:39Z vvoutilainen $
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
21 *
22 * As a special exception, the copyright holders of this library give you
23 * permission to link this library with independent modules to produce an
24 * executable, regardless of the license terms of these independent
25 * modules, and to copy and distribute the resulting executable under
26 * terms of your choice, provided that you also meet, for each linked
27 * independent module, the terms and conditions of the license of that
28 * module.  An independent module is a module which is not derived from
29 * or based on this library.  If you modify this library, you may extend
30 * this exception to your version of the library, but you are not
31 * obligated to do so.  If you do not wish to do so, delete this
32 * exception statement from your version.
33 */
34
35package org.armedbear.lisp;
36
37import java.io.File;
38import java.io.InputStream;
39import java.io.OutputStream;
40import java.io.Reader;
41import java.io.Writer;
42import java.io.FileNotFoundException;
43import java.io.IOException;
44import java.io.RandomAccessFile;
45import org.armedbear.lisp.util.RandomAccessCharacterFile;
46
47public final class FileStream extends Stream
48{
49    private final RandomAccessCharacterFile racf;
50    private final Pathname pathname;
51    private final int bytesPerUnit;
52
53    public FileStream(Pathname pathname, String namestring,
54                      LispObject elementType, LispObject direction,
55                      LispObject ifExists, LispObject format)
56        throws IOException
57    {
58        /* externalFormat is a LispObject of which the first char is a
59         * name of a character encoding (such as :UTF-8 or :ISO-8859-1), used
60         * by ABCL as a string designator, unless the name is :CODE-PAGE.
61         * A real string is (thus) also allowed.
62         *
63         * Then, a property list follows with 3 possible keys:
64         *   :ID (values: code page numbers supported by MS-DOS/IBM-DOS/MS-Windows
65         *   :EOL-STYLE (values: :CR / :LF / :CRLF [none means native])
66         *   :LITTLE-ENDIAN (values: NIL / T)
67         *
68         * These definitions have been taken from FLEXI-STREAMS:
69         *    http://www.weitz.de/flexi-streams/#make-external-format
70         */
71        final File file = new File(namestring);
72        String mode = null;
73        if (direction == Keyword.INPUT) {
74            mode = "r";
75            isInputStream = true;
76        } else if (direction == Keyword.OUTPUT) {
77            mode = "rw";
78            isOutputStream = true;
79        } else if (direction == Keyword.IO) {
80            mode = "rw";
81            isInputStream = true;
82            isOutputStream = true;
83        }
84       
85        Debug.assertTrue(mode != null);
86        RandomAccessFile raf = new RandomAccessFile(file, mode);
87 
88        // ifExists is ignored unless we have an output stream.
89        if (isOutputStream) {
90            final long length = file.isFile() ? file.length() : 0;
91            if (length > 0) {
92                if (ifExists == Keyword.OVERWRITE)
93                    raf.seek(0);
94                else if (ifExists == Keyword.APPEND)
95                    raf.seek(raf.length());
96                else
97                    raf.setLength(0);
98            }
99        }
100        setExternalFormat(format);
101       
102  // don't touch raf directly after passing it to racf.
103  // the state will become inconsistent if you do that.
104        racf = new RandomAccessCharacterFile(raf, encoding);
105
106        this.pathname = pathname;
107        this.elementType = elementType;
108        if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
109            isCharacterStream = true;
110            bytesPerUnit = 1;
111      if (isInputStream) {
112    initAsCharacterInputStream(racf.getReader());
113      }
114      if (isOutputStream) {
115    initAsCharacterOutputStream(racf.getWriter());
116      }
117        } else {
118            isBinaryStream = true;
119            int width;
120            try {
121                width = Fixnum.getValue(elementType.cadr());
122            }
123            catch (ConditionThrowable t) {
124                width = 8;
125            }
126            bytesPerUnit = width / 8;
127      if (isInputStream) {
128    initAsBinaryInputStream(racf.getInputStream());
129      }
130      if (isOutputStream) {
131    initAsBinaryOutputStream(racf.getOutputStream());
132      }
133        }
134    }
135
136    @Override
137    public LispObject typeOf()
138    {
139        return Symbol.FILE_STREAM;
140    }
141
142    @Override
143    public LispObject classOf()
144    {
145        return BuiltInClass.FILE_STREAM;
146    }
147
148    @Override
149    public LispObject typep(LispObject typeSpecifier) throws ConditionThrowable
150    {
151        if (typeSpecifier == Symbol.FILE_STREAM)
152            return T;
153        if (typeSpecifier == BuiltInClass.FILE_STREAM)
154            return T;
155        return super.typep(typeSpecifier);
156    }
157
158    public Pathname getPathname()
159    {
160        return pathname;
161    }
162
163    @Override
164    public LispObject fileLength() throws ConditionThrowable
165    {
166        final long length;
167        if (isOpen()) {
168            try {
169                length = racf.length();
170            }
171            catch (IOException e) {
172                error(new StreamError(this, e));
173                // Not reached.
174                return NIL;
175            }
176        } else {
177            String namestring = pathname.getNamestring();
178            if (namestring == null)
179                return error(new SimpleError("Pathname has no namestring: " +
180                                              pathname.writeToString()));
181            File file = new File(namestring);
182            length = file.length(); // in 8-bit bytes
183        }
184        if (isCharacterStream)
185            return number(length);
186        // "For a binary file, the length is measured in units of the
187        // element type of the stream."
188        return number(length / bytesPerUnit);
189    }
190
191    @Override
192    protected void _unreadChar(int n) throws ConditionThrowable
193    {
194        try {
195            racf.unreadChar((char)n);
196        }
197        catch (IOException e) {
198            error(new StreamError(this, e));
199        }
200    }
201
202    @Override
203    protected boolean _charReady() throws ConditionThrowable
204    {
205        return true;
206    }
207
208    @Override
209    public void _clearInput() throws ConditionThrowable
210    {
211        try {
212      if (isInputStream) {
213    racf.position(racf.length());
214      } else {
215    streamNotInputStream();
216      }
217        }
218        catch (IOException e) {
219            error(new StreamError(this, e));
220        }
221    }
222
223    @Override
224    protected long _getFilePosition() throws ConditionThrowable
225    {
226        try {
227            long pos = racf.position();
228            return pos / bytesPerUnit;
229        }
230        catch (IOException e) {
231            error(new StreamError(this, e));
232            // Not reached.
233            return -1;
234        }
235    }
236
237    @Override
238    protected boolean _setFilePosition(LispObject arg) throws ConditionThrowable
239    {
240        try {
241            long pos;
242            if (arg == Keyword.START)
243                pos = 0;
244            else if (arg == Keyword.END)
245                pos = racf.length();
246            else {
247                long n = Fixnum.getValue(arg); // FIXME arg might be a bignum
248                pos = n * bytesPerUnit;
249            }
250            racf.position(pos);
251        }
252        catch (IOException e) {
253            error(new StreamError(this, e));
254        }
255        return true;
256    }
257
258    @Override
259    public void _close() throws ConditionThrowable
260    {
261        try {
262            racf.close();
263            setOpen(false);
264        }
265        catch (IOException e) {
266            error(new StreamError(this, e));
267        }
268    }
269
270    @Override
271    public String writeToString() throws ConditionThrowable
272    {
273        return unreadableString(Symbol.FILE_STREAM);
274    }
275
276    // ### make-file-stream pathname namestring element-type direction if-exists external-format => stream
277    private static final Primitive MAKE_FILE_STREAM =
278        new Primitive("make-file-stream", PACKAGE_SYS, true,
279                      "pathname namestring element-type direction if-exists external-format")
280    {
281        @Override
282        public LispObject execute(LispObject first, LispObject second,
283                                  LispObject third, LispObject fourth,
284                                  LispObject fifth, LispObject sixth)
285            throws ConditionThrowable
286        {
287            final Pathname pathname;
288            if(first instanceof Pathname) {
289                pathname = (Pathname) first;
290            }
291            else {
292                return type_error(first, Symbol.PATHNAME);
293            }
294            final LispObject namestring = checkString(second);
295            LispObject elementType = third;
296            LispObject direction = fourth;
297            LispObject ifExists = fifth;
298            LispObject externalFormat = sixth;
299           
300            if (direction != Keyword.INPUT && direction != Keyword.OUTPUT &&
301                direction != Keyword.IO)
302                error(new LispError("Direction must be :INPUT, :OUTPUT, or :IO."));
303            try {
304                return new FileStream(pathname, namestring.getStringValue(),
305                                      elementType, direction, ifExists,
306                                      externalFormat);
307            }
308            catch (FileNotFoundException e) {
309                return NIL;
310            }
311            catch (IOException e) {
312                return error(new StreamError(null, e));
313            }
314        }
315    };
316}
Note: See TracBrowser for help on using the repository browser.