source: branches/0.15.x/abcl/src/org/armedbear/lisp/LogicalPathname.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: 12.0 KB
Line 
1/*
2 * LogicalPathname.java
3 *
4 * Copyright (C) 2004-2005 Peter Graves
5 * $Id: LogicalPathname.java 11754 2009-04-12 10:53:39Z vvoutilainen $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import java.util.HashMap;
37import java.util.StringTokenizer;
38
39public final class LogicalPathname extends Pathname
40{
41    private static final String LOGICAL_PATHNAME_CHARS =
42        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-;*.";
43
44    private static final HashMap map = new HashMap();
45
46    public LogicalPathname()
47    {
48    }
49
50    public LogicalPathname(String host, String rest) throws ConditionThrowable
51    {
52        final int limit = rest.length();
53        for (int i = 0; i < limit; i++) {
54            char c = rest.charAt(i);
55            if (LOGICAL_PATHNAME_CHARS.indexOf(c) < 0) {
56                error(new ParseError("The character #\\" + c + " is not valid in a logical pathname."));
57                return;
58            }
59        }
60
61        this.host = new SimpleString(host);
62
63        // "The device component of a logical pathname is always :UNSPECIFIC;
64        // no other component of a logical pathname can be :UNSPECIFIC."
65        device = Keyword.UNSPECIFIC;
66
67        int semi = rest.lastIndexOf(';');
68        if (semi >= 0) {
69            // Directory.
70            String d = rest.substring(0, semi + 1);
71            directory = parseDirectory(d);
72            rest = rest.substring(semi + 1);
73        } else {
74            // "If a relative-directory-marker precedes the directories, the
75            // directory component parsed is as relative; otherwise, the
76            // directory component is parsed as absolute."
77            directory = new Cons(Keyword.ABSOLUTE);
78        }
79
80        int dot = rest.indexOf('.');
81        if (dot >= 0) {
82            String n = rest.substring(0, dot);
83            if (n.equals("*"))
84                name = Keyword.WILD;
85            else
86                name = new SimpleString(n.toUpperCase());
87            rest = rest.substring(dot + 1);
88            dot = rest.indexOf('.');
89            if (dot >= 0) {
90                String t = rest.substring(0, dot);
91                if (t.equals("*"))
92                    type = Keyword.WILD;
93                else
94                    type = new SimpleString(t.toUpperCase());
95                // What's left is the version.
96                String v = rest.substring(dot + 1);
97                if (v.equals("*"))
98                    version = Keyword.WILD;
99                else if (v.equals("NEWEST") || v.equals("newest"))
100                    version = Keyword.NEWEST;
101                else
102                    version = PACKAGE_CL.intern("PARSE-INTEGER").execute(new SimpleString(v));
103            } else {
104                String t = rest;
105                if (t.equals("*"))
106                    type = Keyword.WILD;
107                else
108                    type = new SimpleString(t.toUpperCase());
109            }
110        } else {
111            String n = rest;
112            if (n.equals("*"))
113                name = Keyword.WILD;
114            else if (n.length() > 0)
115                name = new SimpleString(n.toUpperCase());
116        }
117    }
118
119    private static final String LOGICAL_PATHNAME_COMPONENT_CHARS =
120        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-";
121
122    public static final SimpleString canonicalizeStringComponent(AbstractString s)
123        throws ConditionThrowable
124    {
125        final int limit = s.length();
126        for (int i = 0; i < limit; i++) {
127            char c = s.charAt(i);
128            if (LOGICAL_PATHNAME_COMPONENT_CHARS.indexOf(c) < 0) {
129                error(new ParseError("Invalid character #\\" + c +
130                                      " in logical pathname component \"" + s +
131                                      '"'));
132                // Not reached.
133                return null;
134            }
135        }
136        return new SimpleString(s.getStringValue().toUpperCase());
137    }
138
139    public static Pathname translateLogicalPathname(LogicalPathname pathname)
140        throws ConditionThrowable
141    {
142        return (Pathname) Symbol.TRANSLATE_LOGICAL_PATHNAME.execute(pathname);
143    }
144
145    private static final LispObject parseDirectory(String s)
146        throws ConditionThrowable
147    {
148        LispObject result;
149        if (s.charAt(0) == ';') {
150            result = new Cons(Keyword.RELATIVE);
151            s = s.substring(1);
152        } else
153            result = new Cons(Keyword.ABSOLUTE);
154        StringTokenizer st = new StringTokenizer(s, ";");
155        while (st.hasMoreTokens()) {
156            String token = st.nextToken();
157            LispObject obj;
158            if (token.equals("*"))
159                obj = Keyword.WILD;
160            else if (token.equals("**"))
161                obj = Keyword.WILD_INFERIORS;
162            else if (token.equals("..")) {
163                if (result.car() instanceof AbstractString) {
164                    result = result.cdr();
165                    continue;
166                }
167                obj= Keyword.UP;
168            } else
169                obj = new SimpleString(token.toUpperCase());
170            result = new Cons(obj, result);
171        }
172        return result.nreverse();
173    }
174
175    @Override
176    public LispObject typeOf()
177    {
178        return Symbol.LOGICAL_PATHNAME;
179    }
180
181    @Override
182    public LispObject classOf()
183    {
184        return BuiltInClass.LOGICAL_PATHNAME;
185    }
186
187    @Override
188    public LispObject typep(LispObject type) throws ConditionThrowable
189    {
190        if (type == Symbol.LOGICAL_PATHNAME)
191            return T;
192        if (type == BuiltInClass.LOGICAL_PATHNAME)
193            return T;
194        return super.typep(type);
195    }
196
197    @Override
198    protected String getDirectoryNamestring() throws ConditionThrowable
199    {
200        FastStringBuffer sb = new FastStringBuffer();
201        // "If a pathname is converted to a namestring, the symbols NIL and
202        // :UNSPECIFIC cause the field to be treated as if it were empty. That
203        // is, both NIL and :UNSPECIFIC cause the component not to appear in
204        // the namestring." 19.2.2.2.3.1
205        if (directory != NIL) {
206            LispObject temp = directory;
207            LispObject part = temp.car();
208            if (part == Keyword.ABSOLUTE) {
209            } else if (part == Keyword.RELATIVE)
210                sb.append(';');
211            else
212                error(new FileError("Unsupported directory component " + part.writeToString() + ".",
213                                     this));
214            temp = temp.cdr();
215            while (temp != NIL) {
216                part = temp.car();
217                if (part instanceof AbstractString)
218                    sb.append(part.getStringValue());
219                else if (part == Keyword.WILD)
220                    sb.append('*');
221                else if (part == Keyword.WILD_INFERIORS)
222                    sb.append("**");
223                else if (part == Keyword.UP)
224                    sb.append("..");
225                else
226                    error(new FileError("Unsupported directory component " + part.writeToString() + ".",
227                                         this));
228                sb.append(';');
229                temp = temp.cdr();
230            }
231        }
232        return sb.toString();
233    }
234
235    @Override
236    public String writeToString() throws ConditionThrowable
237    {
238        final LispThread thread = LispThread.currentThread();
239        boolean printReadably = (Symbol.PRINT_READABLY.symbolValue(thread) != NIL);
240        boolean printEscape = (Symbol.PRINT_ESCAPE.symbolValue(thread) != NIL);
241        FastStringBuffer sb = new FastStringBuffer();
242        if (printReadably || printEscape)
243            sb.append("#P\"");
244        sb.append(host.getStringValue());
245        sb.append(':');
246        if (directory != NIL)
247            sb.append(getDirectoryNamestring());
248        if (name != NIL) {
249            if (name == Keyword.WILD)
250                sb.append('*');
251            else
252                sb.append(name.getStringValue());
253        }
254        if (type != NIL) {
255            sb.append('.');
256            if (type == Keyword.WILD)
257                sb.append('*');
258            else
259                sb.append(type.getStringValue());
260        }
261        if (version.integerp()) {
262            sb.append('.');
263            int base = Fixnum.getValue(Symbol.PRINT_BASE.symbolValue(thread));
264            if (version instanceof Fixnum)
265                sb.append(Integer.toString(((Fixnum)version).value, base).toUpperCase());
266            else if (version instanceof Bignum)
267                sb.append(((Bignum)version).value.toString(base).toUpperCase());
268        } else if (version == Keyword.WILD) {
269            sb.append(".*");
270        } else if (version == Keyword.NEWEST) {
271            sb.append(".NEWEST");
272        }
273        if (printReadably || printEscape)
274            sb.append('"');
275        return sb.toString();
276    }
277
278    // ### canonicalize-logical-host host => canonical-host
279    private static final Primitive CANONICALIZE_LOGICAL_HOST =
280        new Primitive("canonicalize-logical-host", PACKAGE_SYS, true, "host")
281    {
282        @Override
283        public LispObject execute(LispObject arg)
284            throws ConditionThrowable
285        {
286                AbstractString s = checkString(arg);
287                if (s.length() == 0) {
288                    // "The null string, "", is not a valid value for any
289                    // component of a logical pathname." 19.3.2.2
290                    return error(new LispError("Invalid logical host name: \"" +
291                                                s.getStringValue() + '"'));
292                }
293                return canonicalizeStringComponent(s);
294        }
295    };
296
297    // ### %make-logical-pathname namestring => logical-pathname
298    private static final Primitive _MAKE_LOGICAL_PATHNAME =
299        new Primitive("%make-logical-pathname", PACKAGE_SYS, true, "namestring")
300    {
301        @Override
302        public LispObject execute(LispObject arg)
303            throws ConditionThrowable
304        {
305            // Check for a logical pathname host.
306            String s = arg.getStringValue();
307            String h = getHostString(s);
308            if (h != null) {
309                if (h.length() == 0) {
310                    // "The null string, "", is not a valid value for any
311                    // component of a logical pathname." 19.3.2.2
312                    return error(new LispError("Invalid logical host name: \"" +
313                                                h + '"'));
314                }
315                if (Pathname.LOGICAL_PATHNAME_TRANSLATIONS.get(new SimpleString(h)) != null) {
316                    // A defined logical pathname host.
317                    return new LogicalPathname(h, s.substring(s.indexOf(':') + 1));
318                }
319            }
320            return error(new TypeError("Logical namestring does not specify a host: \"" + s + '"'));
321        }
322    };
323}
Note: See TracBrowser for help on using the repository browser.