DefaultModuleAddress.java
001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002  *
003  * Copyright 2000-2013,  Michael Meyling <mime@qedeq.org>.
004  *
005  * "Hilbert II" is free software; you can redistribute
006  * it and/or modify it under the terms of the GNU General Public
007  * License as published by the Free Software Foundation; either
008  * version 2 of the License, or (at your option) any later version.
009  *
010  * This program is distributed in the hope that it will be useful,
011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013  * GNU General Public License for more details.
014  */
015 
016 package org.qedeq.kernel.se.common;
017 
018 import java.io.File;
019 import java.io.IOException;
020 import java.net.MalformedURLException;
021 import java.net.URL;
022 
023 import org.qedeq.base.io.Path;
024 import org.qedeq.base.io.UrlUtility;
025 import org.qedeq.base.trace.Trace;
026 import org.qedeq.base.utility.StringUtility;
027 import org.qedeq.kernel.se.base.module.LocationList;
028 import org.qedeq.kernel.se.base.module.Specification;
029 
030 
031 /**
032  * An object of this class represents an address for a QEDEQ module.
033  *
034  @author  Michael Meyling
035  */
036 public class DefaultModuleAddress implements ModuleAddress {
037 
038     /** Default memory module address with identifier "default". */
039     public static final DefaultModuleAddress MEMORY = new DefaultModuleAddress();
040 
041     /** This class. */
042     private static final Class CLASS = DefaultModuleAddress.class;
043 
044     /** URL form of this address. */
045     private final String url;
046 
047     /** Header (including protocol, host, port, user) but without file path. */
048     private final String header;
049 
050     /** Path (without protocol, host, port and file name). */
051     private final String path;
052 
053     /** File name of QEDEQ module including <code>.xml</code>. */
054     private final String fileName;
055 
056     /** Is module address relative? */
057     private final boolean relativeAddress;
058 
059     /** Is module address a file? */
060     private final boolean fileAddress;
061 
062     /** Module name. That is file name without <code>.xml</code> */
063     private final String name;
064 
065     /**
066      * Constructor.
067      *
068      @param   u       Address of module. Must not be <code>null</code>.
069      *                  Must be a URL with protocol "file" or "http" and address a file
070      *                  with extension ".xml".
071      @throws  MalformedURLException    Address is formally incorrect.
072      */
073     public DefaultModuleAddress(final String uthrows MalformedURLException {
074         this(u, (DefaultModuleAddressnull);
075     }
076 
077     /**
078      * Constructor.
079      *
080      @param   u       Address of module. Must not be <code>null</code>.
081      *                  Must be a URL with protocol "file" or "http" and address a file
082      *                  with extension ".xml".
083      @throws  MalformedURLException    Address is formally incorrect.
084      */
085     public DefaultModuleAddress(final URL uthrows MalformedURLException {
086         this(u.toExternalForm()(ModuleAddressnull);
087     }
088 
089     /**
090      * Constructor.
091      *
092      @param   file    File path of module. Must address a file
093      *                  with extension ".xml".
094      @throws  IOException Problem with file location.
095      */
096     public DefaultModuleAddress(final File filethrows IOException {
097         this(UrlUtility.toUrl(file.getCanonicalFile()));
098     }
099 
100     /**
101      * Default constructor for memory modules.
102      */
103     public DefaultModuleAddress() {
104         this(true, "default");
105     }
106 
107     /**
108      * Constructor mainly used for memory modules.
109      * TODO 20110227 m31: this is no object oriented design: a parameter must be true???
110      *                    refactor code and create extra memory module address class!
111      *
112      @param   memory      Must be true. If not a runtime exception occurs.
113      @param   identifier  Identifies the module in memory. Must not be <code>null</code>.
114      */
115     public DefaultModuleAddress(final boolean memory, final String identifier) {
116         if (!memory) {
117             throw new IllegalArgumentException("memory must be true");
118         }
119         if (identifier == null) {
120             throw new NullPointerException();
121         }
122         url = "memory://" + identifier;
123         name = identifier;
124         fileAddress = false;
125         fileName = identifier;
126         header = "memory:";
127         path = "";
128         relativeAddress = false;
129     }
130 
131     /**
132      * Constructor.
133      *
134      @param   address  Address of module. Must not be <code>null</code>.
135      *                  Must be a URL with protocol "file" or "http" (if <code>parent</code> is
136      *                  <code>null</code>) and address a file
137      *                  with extension ".xml".
138      @param   parent   Address of parent module. Can be <code>null</code>.
139      @throws  MalformedURLException     Address is formally incorrect.
140      */
141     public DefaultModuleAddress(final String address, final ModuleAddress parent)
142             throws MalformedURLException {
143         final String method = "ModuleAddress(String, ModuleAddress)";
144         if (address == null) {
145             throw new NullPointerException();
146         }
147         URL urmel;
148         try {
149             if (parent != null) {
150                 urmel = new URL(new URL(StringUtility.replace(parent.getUrl()"file://""file:")), address);
151             else {
152                 urmel = new URL(address);
153             }
154         catch (MalformedURLException e) {
155             Trace.trace(CLASS, this, method, "address=" + address);
156             Trace.trace(CLASS, this, method, "parent=" + parent);
157             Trace.trace(CLASS, this, method, e);
158             try {
159                 final String newAddress = "file:" + address;
160                 if (parent != null) {
161                     urmel = new URL(new URL(parent.getUrl()), newAddress);
162                 else {
163                     urmel = new URL(newAddress);
164                 }
165             catch (MalformedURLException ex) {
166                 throw e;    // throw original exception
167             }
168         }
169         final Path p = new Path(urmel.getPath());
170         this.path = p.getDirectory();
171         this.fileName = p.getFileName();
172         Trace.trace(CLASS, this, method, "path=" this.path);
173         Trace.trace(CLASS, this, method, "fileName=" this.fileName);
174         this.relativeAddress = p.isRelative();
175         if (!this.fileName.endsWith(".xml")) {
176             throw new MalformedURLException("file name doesn't end with \".xml\": "
177                 this.fileName);
178         }
179         Trace.trace(CLASS, this, method, "protocol=" + urmel.getProtocol());
180         fileAddress = urmel.getProtocol().equalsIgnoreCase("file");
181         String urm = urmel.toString();
182         Trace.trace(CLASS, this, method, "replacing " + urmel.getPath() " by " + p.toString());
183         urm = StringUtility.replace(urm, urmel.getPath(), p.toString());
184         if (fileAddress) {
185             if (urm.startsWith("file:"&& !urm.startsWith("file://")) {
186                 urm = "file://" + urm.substring("file:".length());
187             }
188         }
189         url = urm;
190         final int positionBefore = this.fileName.lastIndexOf(".");
191         final String mname = this.fileName.substring(0, positionBefore);
192         this.name = mname;
193         final int positionPath = url.lastIndexOf(p.toString());
194         if (positionPath < 0) {
195             throw new IllegalArgumentException(
196                 "couldn't determine begin of file path: "
197                 + url + "\nsearching for: " + p);
198         }
199         this.header = url.substring(0, positionPath);
200     }
201 
202     /**
203      * Get module address as {@link ModuleContext}. Creates a new object.
204      *
205      @return  Module address as {@link ModuleContext}.
206      */
207     public ModuleContext createModuleContext() {
208         return new ModuleContext(this);
209     }
210 
211     /**
212      * Get address header (including protocol, host, port, user)
213      * but without file path.
214      *
215      @return address header
216      */
217     public String getHeader() {
218         return header;
219     }
220 
221     /**
222      * Get address path (without protocol, host, port and file name).
223      *
224      @return module path
225      */
226     public String getPath() {
227         return path;
228     }
229 
230     /**
231      * Get module file name.
232      *
233      @return  Module file name.
234      */
235     public String getFileName() {
236         return fileName;
237     }
238 
239     /**
240      * Get name of module (file name without <code>.xml</code>).
241      *
242      @return  Module name.
243      */
244     public String getName() {
245         return this.name;
246     }
247 
248     /**
249      * Get fully qualified URL of module.
250      *
251      @return  URL for QEDEQ module.
252      */
253     public String getUrl() {
254         return this.url;
255     }
256 
257     /**
258      * Was this module address created relatively?
259      *
260      @return  Relatively created?
261      */
262     public boolean isRelativeAddress() {
263         return this.relativeAddress;
264     }
265 
266     /**
267      * Is this a local QEDEQ file. That means the address starts with <code>file:</code>.
268      *
269      @return  Is the QEDEQ module a local file?
270      */
271     public boolean isFileAddress() {
272         return fileAddress;
273     }
274 
275     public final String toString() {
276         return url;
277     }
278 
279     public final int hashCode() {
280         return url.hashCode();
281     }
282 
283     public final boolean equals(final Object object) {
284         if (!(object instanceof DefaultModuleAddress)) {
285             return false;
286         }
287         return url.equals(((DefaultModuleAddressobject).url);
288     }
289 
290     /**
291      * Get the file name of the specified module.
292      *
293      @param   spec    Here are the (perhaps relative) addresses to
294      *                  another module.
295      @return  File name of specified module.
296      */
297     private static final String getModuleFileName(final Specification spec) {
298 
299         return spec.getName() ".xml";
300     }
301 
302     public final ModuleAddress[] getModulePaths(final Specification specthrows IOException {
303 
304         final String fileNameEnd = getModuleFileName(spec);
305         final LocationList locations = spec.getLocationList();
306         final ModuleAddress[] result
307             new ModuleAddress[(locations != null ? locations.size() 0)];
308         for (int i = 0; i < result.length; i++) {
309             String file = locations.get(i).getLocation();
310             if (file.equals(".")) {
311                 file = "";
312             else if (!file.endsWith("/")) {
313                 file += "/";
314             }
315             file += fileNameEnd;
316             result[inew DefaultModuleAddress(file, this);
317         }
318         return result;
319     }
320 
321     /**
322      * Create relative address from <code>origin</code> to <code>next</code>.
323      * If both addresses point to the same file we return "".
324      *
325      @param   origin  This is the original location (URL!).
326      @param   next    This should be the next location (URL!).
327      @return  Relative (or if necessary absolute) address.
328      */
329     public static final String createRelativeAddress(final String origin,
330             final String next) {
331         if (origin.equals(next)) {
332             return "";
333         }
334         final URL urlOrgin;
335         try {
336             urlOrgin = new URL(origin);
337         catch (MalformedURLException e) {
338             return createRelative(origin, next);
339         }
340         try {
341             final URL urlNext = new URL(next);
342             if (urlOrgin.getProtocol().equals(urlNext.getProtocol())
343                     && urlOrgin.getHost().equals(urlNext.getHost())
344                     && urlOrgin.getPort() == urlNext.getPort()) {
345                 final String org = urlOrgin.getFile();
346                 final String nex = urlNext.getFile();
347                 return createRelative(org, nex);
348             else {    // no relative address possible
349                 return urlNext.toString();
350             }
351         catch (MalformedURLException e) {
352             return next;
353         }
354     }
355 
356     /**
357      * Create relative address. Assume only file paths.
358      *
359      @param   org     This is the original location.
360      @param   nex     This should be the next location.
361      @return  Relative path (if possible).
362      */
363     public static String createRelative(final String org, final String nex) {
364         final Path from = new Path(org);
365         return from.createRelative(nex).toString();
366     }
367 
368 }