source: trunk/abcl/doc/design/pathnames/abcl-pathname.org

Last change on this file was 15464, checked in by Mark Evenson, 3 years ago

abcl 1.8.0 metadata

rc14

TODO manual needs much description about pathnames

File size: 12.3 KB
Line 
1#+TITLE: Design and Implementation of the ABCL PATHNAME
2* The ABCL PATHNAME Implementation
3
4An ongoing document eventually to be published as a paper.
5
6** Needs within ABCL
7
8*** Pathname refactoring
9
10    My original sin consisted in hacking Pathname.java and
11    LogicalPathname.java to contain four distinct Lisp types PATHNAME,
12    LOGICAL-PATHNAME, URL-PATHNAME, and JAR-PATHNAME.
13
14   
15   
16   
17    We want to replace =org.lisp.armedbear.Pathname= with some
18    sort of abstraction that allows easier maintainence and understanding
19    of the code. 
20
21#+caption: Proposed class hierachy
22#+begin_example
23cl:logical-pathname  a cl:pathname
24ext:pathname-url     a cl:pathname
25ext:pathname-jar     a ext:pathname-jar
26#+end_example
27
28**** Analysis
29We naively begin by attempting to outline reasons one can't replace
30with an interface.
31
32***** constructors
33
34These would be present for all =ext:url-pathname=
35
36#+BEGIN_SRC java
37     new Pathname(namestring)
38#+END_SRC
39
40#+BEGIN_SRC java
41    Pathname Pathname.create(namestring)
42#+END_SRC
43
44***** Use Builder or Factory?
45<http://stackoverflow.com/questions/757743/what-is-the-difference-between-builder-design-pattern-and-factory-design-pattern>
46
47Decide to use a modified Builder so we can chain method setting
48invocations to contruct a complicated PATHNAME object by specifying
49one piece of information at a time.
50
51
52#+begin_src java
53      Pathname result = new PathnameBuilder()
54        .setDirectory("/foo/bar/") // I don't think we allow this sort of thing currently
55        .setName("baz")
56        .setType("bat").build();
57#+end_src
58
59In any event, the Pathname constructors would be deprecated, and
60perhaps made =private=.  Currently they are =protected=.
61
62
63***** DONE Encapsulate fields with getter/setters
64     CLOSED: [2020-06-19 Fri 17:42]
65
66     - CLOSING NOTE [2020-06-19 Fri 17:42] \\
67       Done in pathname-2-build.patch
68***** DONE figure out what to do with invalidateNamestring()?
69      CLOSED: [2020-09-22 Tue 12:59]
70
71      - CLOSING NOTE [2020-09-22 Tue 12:59] \\
72        Don't implement a caching strategy:  always recompute.
73Cache result of calling =getNamestring=?
74
75Unsure what this would gain.  For now, just always run the namestring
76computation routine. 
77
78** Description of Current Problems
79
80As noted from <[[file:jar-pathnames.markdown][file:./jar-pathnames.markdown]]>.
81
82Goals:
83
841.  All objects descending from =URL-PATHNAME= can roundtrip their
85    namestring().  WORKING
86
872.  Able to represent archives within archives arbitrarily
88
89   INCOMPLETE: only implementing functionality as it exists post
90   abcl-1.5.0
91
92*** Archives within archives
93
94Figure the hierarchy out abstractly, and then concretely in Java and Lisp.
95
96Idea:  use =DEVICE= components to represent a pathname that is an archive
97
98#+caption: Example of an archive in an archive
99#+begin_example
100jar:jar:file:/abcl/dist/abcl.jar!/something.abcl!/__loader__._
101#+end_example
102
103#+begin_example
104[file:/abcl/dist/abcl.jar]
105  ^--has-device-- [jar:file:/abcl/dist/abcl.jar!/
106     ^--has-device-- [ jar:jar:file:/abcl/dist/abcl.jar!/something.abcl!/]
107        ^--has-device-- [/__loader__._]
108#+end_example
109
110All the following pathnames should be valid:
111#+begin_example
112#p"file:/tmp/foo.jar"
113#p"jar:file:/tmp/foo.jar!/"
114#p"jar:file:/tmp/foo.jar!/a/path/something.abcl"
115#p"jar:file:/tmp/foo.jar!/a/path/something.abcl!/"
116#p"jar:file:/tmp/foo.jar!/a/path/something.abcl!/__loader__._"
117#+end_example
118
119#+NAME: Parsing the namestring
120#+begin_src lisp
121(pathname "jar:jar:file:/tmp/abcl/dist/abcl.jar!/something.abcl!/__loader__._")
122#+end_src
123
124would create four pathnames:
125
126#+begin_src lisp
127  #1# #p(:device #2# :name "__loader__" :type "_")
128
129  #2# #p(:device #3#: :name "something" :type "abcl"
130                 :directory (:absolute))
131
132  #3# #p(:device #4# :name nil :type nil
133         :directory nil :host nil :version nil)
134
135  #4# #p"/tmp/abcl/dist/abcl.jar"
136#+end_src
137
138| reference | namestring                                                         | Java Type    |
139|-----------+--------------------------------------------------------------------+--------------|
140| #1#       | jar:jar:file:/tmp/abcl/dist/abcl.jar!/something.abcl!/__loader__._ | pathname-jar |
141| #2#       | jar:jar:file:/tmp/abcl/dist/abcl.jar!/something.abcl!/             | pathname-jar |
142| #3#       | jar:file:/tmp/abcl/dist/abcl.jar!/                                 | pathname-jar |
143| #4#       | file:/tmp/abcl/dist/abcl.jar                                       | pathname-url |
144
145
146
147#4# has to have a device of nil in order to possibly be a DOS drive letter
148under Windows.
149
150Problems:
151
152#3# is both a file and an archive source.  The namestring of #2#
153encapsulates this, but should a naked reference to #3# be able to be
154target of a DIRECTORY operation?
155
156No, there is a difference between:
157
158| namestring                         | type         |
159|------------------------------------+--------------|
160| jar:file:/tmp/abcl/dist/abcl.jar!/ | pathname-jar |
161| file:/tmp/abcl/dist/abcl.jar       | pathname-url |
162
163
164So, any =JAR-PATHNAME= whose =:directory= is =(:absolute)= can be operated on
165via =MERGE-PATHNAMES= to =DIRECTORY= if it names a valid file or directory.
166
167#+begin_src
168(directory #p"jar:file:/tmp/abcl/dist/abcl.jar!/*.*")
169#+end_src
170
171**** TODO Does this use of =DIRECTORY= clash with current ways of distinguishing files and directories?
172
173*** Fix the representation in CL:PATHNAME of objects to reflect this hierarchy.
174
175IN-PROGRESS mega-patch exists <https://github.com/armedbear/abcl/pull/289> which passes the tests.
176**** TODO Refactor the Java
177
178Use hybrid Builder/Factory pattern.  Don't use constructors, but
179rather =Pathname.create()= and the five =Pathname.setDirectory()=
180=Pathname.setDevice()= calls, which may chained.  This introduces an
181asymmetry between the setCOMPONENT() / getCOMPONENT() entries, but
182seems workable.
183
184
185** TODO Rename existing Java hierarchy?
186
187Too destructive?!
188| current      | new                                                        |
189|--------------+------------------------------------------------------------|
190| pathname-jar | pathname-archive pathname-zip-archive pathname-jar-archive |
191| pathname-url | pathname-url                                               |
192
193* Gotchas
194
195** Should error: "jar:" prefix needs suffixed "!/"
196  #+begin_src
197#p"jar:file:foo.jar" 
198  #+end_src
199
200* Scratch
201** Algorithim to enumerate jars in a namestring
202
203Count the prefixed occurrences of "jar:".
204
205Return
206   
207   The pathname of the root jar as the first value
208   
209   For each enclosed jar, the pathname suffixed with "!/.
210   
211   If there is a path within the last jar, return it as an absolute value
212
213  #+begin_example
214  jar:jar:file:abcl.jar!/time.abcl!/time_1.cls
215       =>
216       file:abcl.jar
217       /time.abcl!/
218       /time_1.cls
219  #+end_example
220
221  #+begin_example
222  jar:jar:https://abcl.org/releases/current/abcl.jar!/a-fasl.abcl!/__loader__._
223       =>
224       https://abcl.org/releases/current/abcl.jar!/
225       /a-fasl.abcl!/
226       /__loader__._
227  #+end_example
228
229  #+begin_example
230  jar:jar:jar:file:abcl-aio.jar!/abcl-contrib.jar!/enclosed.abcl!/__loader__._
231       =>
232       file:abcl-aio.jar
233       /abcl-contrib.jar!/
234       /enclosed.abcl!/
235       /__loader__._
236  #+end_example
237
238* Tests
239** Problem with abcl-1.5.0
240
241   #+begin_src
242   #p"jar:jar:file:/a/baz.jar!/b/c/foo.abcl!/"
243   #+end_src
244
245Refers to three =CL:PATHNAME= objects:
246
247|-----+-----------------------------------------+--------+--------------|
248| Ref | Namestring                              | Device | Type         |
249|-----+-----------------------------------------+--------+--------------|
250| #1# | file:/a/baz.jar                         | nil    | PATHNAME-URL |
251| #2# | jar:file:/a/baz.jar!/                   | #1#    | PATHNAME-JAR |
252| #3# | jar:jar:file:/a/baz.jar!/b/c/foo.abcl!/ | #2#    | PATHNAME-JAR |
253|-----+-----------------------------------------+--------+--------------|
254
255   #+begin_src
256   #p"jar:jar:file:/a/baz.jar!/b/c/foo.abcl!/a.cls"
257   #+end_src
258
259|-----+----------------------------------------------+--------+--------------|
260| Ref | Namestring                                   | Device | Type         |
261|-----+----------------------------------------------+--------+--------------|
262| #1# | file:/a/baz.jar                              | nil    | PATHNAME-URL |
263| #2# | jar:file:/a/baz.jar!/                        | #1#    | PATHNAME-JAR |
264| #3# | jar:jar:file:/a/baz.jar!/b/c/foo.abcl!/      | #2#    | PATHNAME-JAR |
265| #4# | jar:jar:file:/a/baz.jar!/b/c/foo.abcl!/a.cls | #3#    | PATHNAME-JAR |
266|-----+----------------------------------------------+--------+--------------|
267
268#+begin_src
269#p"jar:file:foo.jar!/bar.abcl"
270#+end_src
271
272|-----+----------------------------+--------+--------------|
273| Ref | Namestring                 | Device | Type         |
274|-----+----------------------------+--------+--------------|
275| #1# | file:foo.jar               | nil    | PATHNAME-URL |
276| #2# | jar:file:foo.jar!/bar.abcl | #1#    | PATHNAME-JAR |
277
278
279** From the ABCL junit tests
280
281*** TODO Necessary for ASDF jar translations to work
282
283   #+begin_src
284   #p"jar:file:/**/*.jar!/**/*.*"
285   #+end_src
286
287|-----+----------------------------+--------+--------------|
288| Ref | Namestring                 | Device | Type         |
289|-----+----------------------------+--------+--------------|
290| #1# | file:/**/*.jar             | nil    | PATHNAME-URL |
291| #2# | jar:file:/**/*.jar!/       | #1#    | PATHNAME-JAR |
292| #3# | jar:file:/**/*.jar!/**/*.* | #2#    | PATHNAME-JAR |
293|-----+----------------------------+--------+--------------|
294
295
296*** Merging
297
298A =PATHNAME_JAR= may have its root jar as a relative pathname in order
299to merge things succesfully.
300
301
302   #+begin_src java
303   Pathname p = (Pathname)Pathname.create("jar:file:foo.jar!/bar.abcl");
304   Pathname d = (Pathname)Pathname.create("/a/b/c/");
305   Pathname r = (Pathname)Pathname.mergePathnames(p, d);
306   String s = r.getNamestring();
307   assertTrue(s.equals("jar:file:/a/b/c/foo.jar!/bar.abcl"));
308   #+end_src
309
310| "jar:file:foo.jar!/bar.abcl"       | addressing bar.abcl as a file |
311| "jar:jar:file:foo.jar!/bar.abcl!/" | addressing bar.abcl as a jar  |
312|                                    |                               |
313
314#+begin_src lisp
315(merge-pathnames "jar:file:foo.jar!/bar.abcl" "/a/b/c/")
316#+end_src
317
318What do we do when MERGE-PATHNAME gets two PATHNAME-JAR arguments?
319
320#+begin_src lisp
321(merge-pathname "jar:file:abcl-contrib.jar!/init.lisp"
322                "jar:file:/a/b/abcl.jar!/")
323#+end_src
324==>
325"jar:jar:file:/a/b/abcl.jar!/abcl-contrib.jar/init.lisp"
326
327#+begin_src lisp
328(merge-pathname "jar:file:/abcl-contrib.jar!/init.lisp"
329                "jar:file:/a/b/abcl.jar!/foo/jar")
330#+end_src
331==>
332"jar:file:/abcl-contrib.jar!/init.lisp"
333
334
335This one I no longer understand
336#+begin_src lisp
337(merge-pathname "jar:file:!/init.lisp"
338                "jar:file:/a/b/abcl.jar!/load/path/")
339#+end_src
340==>
341"jar:file:/a/b/abcl.jar!/load/path/init.lisp"
342
343Should be
344#+begin_src lisp
345(merge-pathname "init.lisp"
346                "jar:file:/a/b/abcl.jar!/load/path/")
347#+end_src
348==>
349"jar:file:/a/b/abcl.jar!/load/path/init.lisp"
350
351* Misc
352** PATHNAME-URL have implicit "file:" scheme
353Not recorded in host; not emitted as namestring.  This is the current behavior.
354
355* Have to rework?
356
357Unfortunately using a chain of devices to represent things doesn't
358seem to work.
359
360How to repesent the difference between the two?
361
362| #1# | "jar:jar:file:abcl.jar!/a/fasl.abcl!/" |
363| #2# | "jar:file:abcl.jar!/a/fasl.abcl"       |
364
365They both denote an entry in an archive.
366
367#1# denotes the "archive within an archive", something that could be
368as the defaults for a merge pathnames operation.  Or that
369=CL:DIRECTORY= could return hte contents thereof.
370
371#2# denotes the entry as something that could be =CL:OPEN='d. 
372
373But under the current proposal, both would be represented as a
374PATHNAME-JAR whose device was "jar:file:abcl.jar".
375
376If we go back to storing the list of all jar locations in the device
377component, they would look like
378
379#1# (:device ("abcl.jar" "/a/fasl.abcl"))
380
381#2# (:device ("abcl.jar) :name "fasl" :type "abcl")
382
383** What should the type of the pathnames be in the DEVICE?
384
385Even though these are references to paths within jars, they aren't a
386PATHNAME-JAR (they don't have a DEVICE which is a cons), so just make
387them pathnames.
388
389
390* Re-introducing relative URL-PATHNAME for 'file' scheme
391
392URIs don't allow relative pathnames, so to be more strict I
393implemented stripped out the abilty to create relative URL-PATHNAMEs.
394
395
396
397
398
399* Colophon
400  #+begin_example
401  Mark Evenson 
402  Created: 2010
403  Revised: <2020-08-15 Sat 10:06>
404  #+end_example
405
Note: See TracBrowser for help on using the repository browser.