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

Last change on this file was 13444, checked in by ehuelsmann, 13 years ago

Reduce the number of required unreadableString() methods by removing
the ones taking a symbol argument: all invocations involved a constant
parameter, now replaced with a constant string instead.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 10.9 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 13444 2011-08-06 13:51:26Z ehuelsmann $
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 static org.armedbear.lisp.Lisp.*;
38
39import java.io.File;
40import java.io.InputStream;
41import java.io.OutputStream;
42import java.io.Reader;
43import java.io.Writer;
44import java.io.FileNotFoundException;
45import java.io.IOException;
46import java.io.RandomAccessFile;
47import org.armedbear.lisp.util.RandomAccessCharacterFile;
48
49public final class FileStream extends Stream
50{
51    private final RandomAccessCharacterFile racf;
52    private final Pathname pathname;
53    private final int bytesPerUnit;
54
55    public FileStream(Pathname pathname, String namestring,
56                      LispObject elementType, LispObject direction,
57                      LispObject ifExists, LispObject format)
58        throws IOException
59    {
60        /* externalFormat is a LispObject of which the first char is a
61         * name of a character encoding (such as :UTF-8 or :ISO-8859-1), used
62         * by ABCL as a string designator, unless the name is :CODE-PAGE.
63         * A real string is (thus) also allowed.
64         *
65         * Then, a property list follows with 3 possible keys:
66         *   :ID (values: code page numbers supported by MS-DOS/IBM-DOS/MS-Windows
67         *   :EOL-STYLE (values: :CR / :LF / :CRLF [none means native])
68         *   :LITTLE-ENDIAN (values: NIL / T)
69         *
70         * These definitions have been taken from FLEXI-STREAMS:
71         *    http://www.weitz.de/flexi-streams/#make-external-format
72         */
73        super(Symbol.FILE_STREAM);
74        final File file = new File(namestring);
75        String mode = null;
76        if (direction == Keyword.INPUT) {
77            mode = "r";
78            isInputStream = true;
79        } else if (direction == Keyword.OUTPUT) {
80            mode = "rw";
81            isOutputStream = true;
82        } else if (direction == Keyword.IO) {
83            mode = "rw";
84            isInputStream = true;
85            isOutputStream = true;
86        }
87       
88        Debug.assertTrue(mode != null);
89        RandomAccessFile raf = new RandomAccessFile(file, mode);
90 
91        // ifExists is ignored unless we have an output stream.
92        if (isOutputStream) {
93            final long length = file.isFile() ? file.length() : 0;
94            if (length > 0) {
95                if (ifExists == Keyword.OVERWRITE)
96                    raf.seek(0);
97                else if (ifExists == Keyword.APPEND)
98                    raf.seek(raf.length());
99                else
100                    raf.setLength(0);
101            }
102        }
103        setExternalFormat(format);
104       
105  // don't touch raf directly after passing it to racf.
106  // the state will become inconsistent if you do that.
107        racf = new RandomAccessCharacterFile(raf, encoding);
108
109        this.pathname = pathname;
110        this.elementType = elementType;
111        if (elementType == Symbol.CHARACTER || elementType == Symbol.BASE_CHAR) {
112            isCharacterStream = true;
113            bytesPerUnit = 1;
114      if (isInputStream) {
115    initAsCharacterInputStream(racf.getReader());
116      }
117      if (isOutputStream) {
118    initAsCharacterOutputStream(racf.getWriter());
119      }
120        } else {
121            isBinaryStream = true;
122            int width = Fixnum.getValue(elementType.cadr());
123            bytesPerUnit = width / 8;
124      if (isInputStream) {
125    initAsBinaryInputStream(racf.getInputStream());
126      }
127      if (isOutputStream) {
128    initAsBinaryOutputStream(racf.getOutputStream());
129      }
130        }
131    }
132
133    @Override
134    public LispObject typeOf()
135    {
136        return Symbol.FILE_STREAM;
137    }
138
139    @Override
140    public LispObject classOf()
141    {
142        return BuiltInClass.FILE_STREAM;
143    }
144
145    @Override
146    public LispObject typep(LispObject typeSpecifier)
147    {
148        if (typeSpecifier == Symbol.FILE_STREAM)
149            return T;
150        if (typeSpecifier == BuiltInClass.FILE_STREAM)
151            return T;
152        return super.typep(typeSpecifier);
153    }
154
155    @Override
156    public void setExternalFormat(LispObject format) {
157        super.setExternalFormat(format);
158
159        if (racf != null)
160            // setExternalFormat also called before 'racf' is set up
161            racf.setEncoding(encoding);
162    }
163
164    public Pathname getPathname()
165    {
166        return pathname;
167    }
168
169    @Override
170    public LispObject fileLength()
171    {
172        final long length;
173        if (isOpen()) {
174            try {
175                length = racf.length();
176            }
177            catch (IOException e) {
178                error(new StreamError(this, e));
179                // Not reached.
180                return NIL;
181            }
182        } else {
183            String namestring = pathname.getNamestring();
184            if (namestring == null)
185                return error(new SimpleError("Pathname has no namestring: " +
186                                              pathname.princToString()));
187            File file = new File(namestring);
188            length = file.length(); // in 8-bit bytes
189        }
190        if (isCharacterStream)
191            return number(length);
192        // "For a binary file, the length is measured in units of the
193        // element type of the stream."
194        return number(length / bytesPerUnit);
195    }
196
197    @Override
198    protected boolean _charReady()
199    {
200        return true;
201    }
202
203    @Override
204    public void _clearInput()
205    {
206        try {
207      if (isInputStream) {
208    racf.position(racf.length());
209      } else {
210    streamNotInputStream();
211      }
212        }
213        catch (IOException e) {
214            error(new StreamError(this, e));
215        }
216    }
217
218    @Override
219    protected long _getFilePosition()
220    {
221        try {
222            long pos = racf.position();
223            return pos / bytesPerUnit;
224        }
225        catch (IOException e) {
226            error(new StreamError(this, e));
227            // Not reached.
228            return -1;
229        }
230    }
231
232    @Override
233    protected boolean _setFilePosition(LispObject arg)
234    {
235        try {
236            long pos;
237            if (arg == Keyword.START)
238                pos = 0;
239            else if (arg == Keyword.END)
240                pos = racf.length();
241            else {
242                long n = Fixnum.getValue(arg); // FIXME arg might be a bignum
243                pos = n * bytesPerUnit;
244            }
245            racf.position(pos);
246        }
247        catch (IOException e) {
248            error(new StreamError(this, e));
249        }
250        return true;
251    }
252
253    @Override
254    public void _close()
255    {
256        try {
257            racf.close();
258            setOpen(false);
259        }
260        catch (IOException e) {
261            error(new StreamError(this, e));
262        }
263    }
264
265    @Override
266    public String printObject()
267    {
268        return unreadableString("FILE-STREAM");
269    }
270
271    // ### make-file-stream pathname namestring element-type direction if-exists external-format => stream
272    private static final Primitive MAKE_FILE_STREAM =
273        new Primitive("make-file-stream", PACKAGE_SYS, true,
274                      "pathname namestring element-type direction if-exists external-format")
275    {
276        @Override
277        public LispObject execute(LispObject first, LispObject second,
278                                  LispObject third, LispObject fourth,
279                                  LispObject fifth, LispObject sixth)
280
281        {
282            final Pathname pathname;
283            if (first instanceof Pathname) {
284                pathname = (Pathname) first;
285            }
286            else {
287                return type_error(first, Symbol.PATHNAME);
288            }
289            final LispObject namestring = checkString(second);
290            LispObject elementType = third;
291            LispObject direction = fourth;
292            LispObject ifExists = fifth;
293            LispObject externalFormat = sixth;
294           
295            if (direction != Keyword.INPUT && direction != Keyword.OUTPUT &&
296                direction != Keyword.IO)
297                error(new LispError("Direction must be :INPUT, :OUTPUT, or :IO."));
298
299            if (pathname.isJar())  {
300                if (direction != Keyword.INPUT) {
301                    error(new FileError("Only direction :INPUT is supported for jar files.", pathname));
302                }
303                try { 
304                    return new JarStream(pathname, namestring.getStringValue(),
305                                         elementType, direction, ifExists,
306                                         externalFormat);
307                } catch (IOException e) {
308                    return error(new StreamError(null, e));
309                }
310            } else if (pathname.isURL()) {
311                if (direction != Keyword.INPUT) {
312                    error(new FileError("Only direction :INPUT is supported for URLs.", pathname));
313                }
314                try { 
315                    return new URLStream(pathname, namestring.getStringValue(),
316                                         elementType, direction, ifExists,
317                                         externalFormat);
318                } catch (IOException e) {
319                    return error(new StreamError(null, e));
320                }
321            } else {
322                try {
323                    return new FileStream(pathname, namestring.getStringValue(),
324                                          elementType, direction, ifExists,
325                                          externalFormat);
326                }
327                catch (FileNotFoundException e) {
328                    return NIL;
329                }
330                catch (IOException e) {
331                    return error(new StreamError(null, e));
332                }
333            }
334        }
335    };
336}
Note: See TracBrowser for help on using the repository browser.