View Javadoc

1   /* This file is part of the project "Hilbert II" - http://www.qedeq.org" target="alexandria_uri">http://www.qedeq.org
2    *
3    * Copyright 2000-2014,  Michael Meyling <mime@qedeq.org>.
4    *
5    * "Hilbert II" is free software; you can redistribute
6    * it and/or modify it under the terms of the GNU General Public
7    * License as published by the Free Software Foundation; either
8    * version 2 of the License, or (at your option) any later version.
9    *
10   * This program is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13   * GNU General Public License for more details.
14   */
15  
16  package org.qedeq.kernel.se.common;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.net.MalformedURLException;
21  import java.net.URL;
22  import java.util.ArrayList;
23  import java.util.List;
24  
25  import org.qedeq.base.io.Path;
26  import org.qedeq.base.io.UrlUtility;
27  import org.qedeq.base.trace.Trace;
28  import org.qedeq.base.utility.StringUtility;
29  import org.qedeq.kernel.se.base.module.LocationList;
30  import org.qedeq.kernel.se.base.module.Specification;
31  
32  
33  /**
34   * An object of this class represents an address for a QEDEQ module.
35   *
36   * @author  Michael Meyling
37   */
38  public class DefaultModuleAddress implements ModuleAddress {
39  
40      /** Default memory module address with identifier "default". */
41      public static final DefaultModuleAddress MEMORY = new DefaultModuleAddress();
42  
43      /** This class. */
44      private static final Class CLASS = DefaultModuleAddress.class;
45  
46      /** URL form of this address. */
47      private final String url;
48  
49      /** Header (including protocol, host, port, user) but without file path. */
50      private final String header;
51  
52      /** Path (without protocol, host, port and file name). */
53      private final String path;
54  
55      /** File name of QEDEQ module including <code>.xml</code>. */
56      private final String fileName;
57  
58      /** Is module address relative? */
59      private final boolean relativeAddress;
60  
61      /** Is module address a file? */
62      private final boolean fileAddress;
63  
64      /** Module name. That is file name without <code>.xml</code> */
65      private final String name;
66  
67      /**
68       * Constructor.
69       *
70       * @param   u       Address of module. Must not be <code>null</code>.
71       *                  Must be a URL with protocol "file" or "http" and address a file
72       *                  with extension ".xml".
73       * @throws  MalformedURLException    Address is formally incorrect.
74       */
75      public DefaultModuleAddress(final String u) throws MalformedURLException {
76          this(u, (DefaultModuleAddress) null);
77      }
78  
79      /**
80       * Constructor.
81       *
82       * @param   u       Address of module. Must not be <code>null</code>.
83       *                  Must be a URL with protocol "file" or "http" and address a file
84       *                  with extension ".xml".
85       * @throws  MalformedURLException    Address is formally incorrect.
86       */
87      public DefaultModuleAddress(final URL u) throws MalformedURLException {
88          this(u.toExternalForm(), (ModuleAddress) null);
89      }
90  
91      /**
92       * Constructor.
93       *
94       * @param   file    File path of module. Must address a file
95       *                  with extension ".xml".
96       * @throws  IOException Problem with file location.
97       */
98      public DefaultModuleAddress(final File file) throws IOException {
99          this(UrlUtility.toUrl(file.getCanonicalFile()));
100     }
101 
102     /**
103      * Default constructor for memory modules.
104      */
105     public DefaultModuleAddress() {
106         this(true, "default");
107     }
108 
109     /**
110      * Constructor mainly used for memory modules.
111      * TODO 20110227 m31: this is no object oriented design: a parameter must be true???
112      *                    refactor code and create extra memory module address class!
113      *
114      * @param   memory      Must be true. If not a runtime exception occurs.
115      * @param   identifier  Identifies the module in memory. Must not be <code>null</code>.
116      */
117     public DefaultModuleAddress(final boolean memory, final String identifier) {
118         if (!memory) {
119             throw new IllegalArgumentException("memory must be true");
120         }
121         if (identifier == null) {
122             throw new NullPointerException();
123         }
124         url = "memory://" + identifier;
125         name = identifier;
126         fileAddress = false;
127         fileName = identifier;
128         header = "memory:";
129         path = "";
130         relativeAddress = false;
131     }
132 
133     /**
134      * Constructor.
135      *
136      * @param   address  Address of module. Must not be <code>null</code>.
137      *                  Must be a URL with protocol "file" or "http" (if <code>parent</code> is
138      *                  <code>null</code>) and address a file
139      *                  with extension ".xml".
140      * @param   parent   Address of parent module. Can be <code>null</code>.
141      * @throws  MalformedURLException     Address is formally incorrect.
142      */
143     public DefaultModuleAddress(final String address, final ModuleAddress parent)
144             throws MalformedURLException {
145         final String method = "ModuleAddress(String, ModuleAddress)";
146         if (address == null) {
147             throw new NullPointerException();
148         }
149         URL urmel;
150         try {
151             if (parent != null) {
152                 urmel = new URL(new URL(StringUtility.replace(parent.getUrl(), "file://", "file:")), address);
153             } else {
154                 urmel = new URL(address);
155             }
156         } catch (MalformedURLException e) {
157             Trace.trace(CLASS, this, method, "address=" + address);
158             Trace.trace(CLASS, this, method, "parent=" + parent);
159             Trace.trace(CLASS, this, method, e);
160             try {
161                 final String newAddress = "file:" + address;
162                 if (parent != null) {
163                     urmel = new URL(new URL(parent.getUrl()), newAddress);
164                 } else {
165                     urmel = new URL(newAddress);
166                 }
167             } catch (MalformedURLException ex) {
168                 throw e;    // throw original exception
169             }
170         }
171         final Path p = new Path(urmel.getPath());
172         this.path = p.getDirectory();
173         this.fileName = p.getFileName();
174         Trace.trace(CLASS, this, method, "path=" + this.path);
175         Trace.trace(CLASS, this, method, "fileName=" + this.fileName);
176         this.relativeAddress = p.isRelative();
177         if (!this.fileName.endsWith(".xml")) {
178             throw new MalformedURLException("file name doesn't end with \".xml\": "
179                 + this.fileName);
180         }
181         Trace.trace(CLASS, this, method, "protocol=" + urmel.getProtocol());
182         fileAddress = urmel.getProtocol().equalsIgnoreCase("file");
183         String urm = urmel.toString();
184         Trace.trace(CLASS, this, method, "replacing " + urmel.getPath() + " by " + p.toString());
185         urm = StringUtility.replace(urm, urmel.getPath(), p.toString());
186         if (fileAddress) {
187             if (urm.startsWith("file:") && !urm.startsWith("file://")) {
188                 urm = "file://" + urm.substring("file:".length());
189             }
190         }
191         url = urm;
192         final int positionBefore = this.fileName.lastIndexOf(".");
193         final String mname = this.fileName.substring(0, positionBefore);
194         this.name = mname;
195         final int positionPath = url.lastIndexOf(p.toString());
196         if (positionPath < 0) {
197             throw new IllegalArgumentException(
198                 "couldn't determine begin of file path: "
199                 + url + "\nsearching for: " + p);
200         }
201         this.header = url.substring(0, positionPath);
202     }
203 
204     /**
205      * Get module address as {@link ModuleContext}. Creates a new object.
206      *
207      * @return  Module address as {@link ModuleContext}.
208      */
209     public ModuleContext createModuleContext() {
210         return new ModuleContext(this);
211     }
212 
213     /**
214      * Get address header (including protocol, host, port, user)
215      * but without file path.
216      *
217      * @return address header
218      */
219     public String getHeader() {
220         return header;
221     }
222 
223     /**
224      * Get address path (without protocol, host, port and file name).
225      *
226      * @return module path
227      */
228     public String getPath() {
229         return path;
230     }
231 
232     /**
233      * Get module file name.
234      *
235      * @return  Module file name.
236      */
237     public String getFileName() {
238         return fileName;
239     }
240 
241     /**
242      * Get name of module (file name without <code>.xml</code>).
243      *
244      * @return  Module name.
245      */
246     public String getName() {
247         return this.name;
248     }
249 
250     /**
251      * Get fully qualified URL of module.
252      *
253      * @return  URL for QEDEQ module.
254      */
255     public String getUrl() {
256         return this.url;
257     }
258 
259     /**
260      * Was this module address created relatively?
261      *
262      * @return  Relatively created?
263      */
264     public boolean isRelativeAddress() {
265         return this.relativeAddress;
266     }
267 
268     /**
269      * Is this a local QEDEQ file. That means the address starts with <code>file:</code>.
270      *
271      * @return  Is the QEDEQ module a local file?
272      */
273     public boolean isFileAddress() {
274         return fileAddress;
275     }
276 
277     public final String toString() {
278         return url;
279     }
280 
281     public final int hashCode() {
282         return url.hashCode();
283     }
284 
285     public final boolean equals(final Object object) {
286         if (!(object instanceof DefaultModuleAddress)) {
287             return false;
288         }
289         return url.equals(((DefaultModuleAddress) object).url);
290     }
291 
292     /**
293      * Get the file name of the specified module.
294      *
295      * @param   spec    Here are the (perhaps relative) addresses to
296      *                  another module.
297      * @return  File name of specified module.
298      */
299     private static final String getModuleFileName(final Specification spec) {
300 
301         return spec.getName() + ".xml";
302     }
303 
304     public final ModuleAddress[] getModulePaths(final Specification spec) throws IOException {
305 
306         final String fileNameEnd = getModuleFileName(spec);
307         final LocationList locations = spec.getLocationList();
308         final List result = new ArrayList();
309         for (int i = 0; locations != null && i < locations.size(); i++) {
310             if (locations.get(i) == null) {
311                 continue;
312             }
313             String file = locations.get(i).getLocation();
314             if (file.equals(".")) {
315                 file = "";
316             } else if (!file.endsWith("/")) {
317                 file += "/";
318             }
319             file += fileNameEnd;
320             result.add(new DefaultModuleAddress(file, this));
321         }
322         return (ModuleAddress[]) result.toArray(new ModuleAddress[] {});
323     }
324 
325     /**
326      * Create relative address from <code>origin</code> to <code>next</code>.
327      * If both addresses point to the same file we return "".
328      *
329      * @param   origin  This is the original location (URL!).
330      * @param   next    This should be the next location (URL!).
331      * @return  Relative (or if necessary absolute) address.
332      */
333     public static final String createRelativeAddress(final String origin,
334             final String next) {
335         if (origin.equals(next)) {
336             return "";
337         }
338         final URL urlOrgin;
339         try {
340             urlOrgin = new URL(origin);
341         } catch (MalformedURLException e) {
342             return createRelative(origin, next);
343         }
344         try {
345             final URL urlNext = new URL(next);
346             if (urlOrgin.getProtocol().equals(urlNext.getProtocol())
347                     && urlOrgin.getHost().equals(urlNext.getHost())
348                     && urlOrgin.getPort() == urlNext.getPort()) {
349                 final String org = urlOrgin.getFile();
350                 final String nex = urlNext.getFile();
351                 return createRelative(org, nex);
352             }
353             // no relative address possible
354             return urlNext.toString();
355         } catch (MalformedURLException e) {
356             return next;
357         }
358     }
359 
360     /**
361      * Create relative address. Assume only file paths.
362      *
363      * @param   org     This is the original location.
364      * @param   nex     This should be the next location.
365      * @return  Relative path (if possible).
366      */
367     public static String createRelative(final String org, final String nex) {
368         final Path from = new Path(org);
369         return from.createRelative(nex).toString();
370     }
371 
372 }
373