DefaultModuleAddress.java
001 /* This file is part of the project "Hilbert II" - http://www.qedeq.org
002  *
003  * Copyright 2000-2011,  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.IoUtility;
024 import org.qedeq.base.trace.Trace;
025 import org.qedeq.kernel.se.base.module.LocationList;
026 import org.qedeq.kernel.se.base.module.Specification;
027 
028 
029 /**
030  * An object of this class represents an address for a QEDEQ module.
031  *
032  @author  Michael Meyling
033  */
034 public class DefaultModuleAddress implements ModuleAddress {
035 
036     /** This class. */
037     private static final Class CLASS = DefaultModuleAddress.class;
038 
039     /** URL form of this address. */
040     private final String url;
041 
042     /** Header (including protocol, host, port, user) but without file path. */
043     private final String header;
044 
045     /** Path (without protocol, host, port and file name). */
046     private final String path;
047 
048     /** File name of QEDEQ module including <code>.xml</code>. */
049     private final String fileName;
050 
051     /** Is module address relative? */
052     private final boolean relativeAddress;
053 
054     /** Is module address a file? */
055     private final boolean fileAddress;
056 
057     /** Module name. That is file name without <code>.xml</code> */
058     private final String name;
059 
060     /**
061      * Constructor.
062      *
063      @param   u       Address of module. Must not be <code>null</code>.
064      *                  Must be a URL with protocol "file" or "http" and address a file
065      *                  with extension ".xml".
066      @throws  MalformedURLException    Address is formally incorrect.
067      */
068     public DefaultModuleAddress(final String uthrows MalformedURLException {
069         this(u, (DefaultModuleAddressnull);
070     }
071 
072     /**
073      * Constructor.
074      *
075      @param   u       Address of module. Must not be <code>null</code>.
076      *                  Must be a URL with protocol "file" or "http" and address a file
077      *                  with extension ".xml".
078      @throws  MalformedURLException    Address is formally incorrect.
079      */
080     public DefaultModuleAddress(final URL uthrows MalformedURLException {
081         this(u.toExternalForm()(ModuleAddressnull);
082     }
083 
084     /**
085      * Constructor.
086      *
087      @param   file    File path of module. Must address a file
088      *                  with extension ".xml".
089      @throws  IOException Problem with file location.
090      */
091     public DefaultModuleAddress(final File filethrows IOException {
092         this(IoUtility.toUrl(file.getCanonicalFile()));
093     }
094 
095     /**
096      * Default constructor for memory modules.
097      */
098     public DefaultModuleAddress() {
099         this(true, "default");
100     }
101 
102     /**
103      * Constructor mainly used for memory modules.
104      * FIXME 20110227 m31: this is no object oriented design: a parameter must be true???
105      *                    refactor code and create extra memory module address class!
106      *
107      @param   memory      Must be true. If not a runtime exception occurs.
108      @param   identifier  Identifies the module in memory.
109      */
110     public DefaultModuleAddress(final boolean memory, final String identifier) {
111         if (!memory) {
112             throw new IllegalArgumentException("memory must be true");
113         }
114         url = "memory:" + identifier;
115         name = identifier;
116         fileAddress = false;
117         fileName = identifier;
118         header = "memory:";
119         path = "";
120         relativeAddress = true;
121     }
122 
123     /**
124      * Constructor.
125      *
126      @param   address  Address of module. Must not be <code>null</code>.
127      *                  Must be a URL with protocol "file" or "http" (if <code>parent</code> is
128      *                  <code>null</code>) and address a file
129      *                  with extension ".xml".
130      @param   parent   Address of parent module. Can be <code>null</code>.
131      @throws  MalformedURLException     Address is formally incorrect.
132      */
133     public DefaultModuleAddress(final String address, final ModuleAddress parent)
134             throws MalformedURLException {
135         final String method = "ModuleAddress(String, ModuleAddress)";
136         if (address == null) {
137             throw new NullPointerException();
138         }
139         URL urmel;
140         try {
141             if (parent != null) {
142                 urmel = new URL(new URL(parent.getUrl()), address);
143             else {
144                 urmel = new URL(address);
145             }
146         catch (MalformedURLException e) {
147             Trace.trace(CLASS, this, method, "address=" + address);
148             Trace.trace(CLASS, this, method, "parent=" + parent);
149             Trace.trace(CLASS, this, method, e);
150             try {
151                 final String newAddress = "file:" + address;
152                 if (parent != null) {
153                     urmel = new URL(new URL(parent.getUrl()), newAddress);
154                 else {
155                     urmel = new URL(newAddress);
156                 }
157             catch (MalformedURLException ex) {
158                 throw e;    // throw original exception
159             }
160         }
161         Trace.trace(CLASS, this, method, "protocol=" + urmel.getProtocol());
162         url = urmel.toString();
163         fileAddress = urmel.getProtocol().equalsIgnoreCase("file");
164 /*
165         Trace.trace(this, METHOD, "url.getFile=" + this.url.getFile());
166         Trace.trace(this, METHOD, "url.getPath=" + this.url.getPath());
167         try {
168             Trace.trace(this, METHOD, "URI File=" +
169                 new File(new URI(this.address)).getAbsoluteFile());
170         } catch (URISyntaxException e1) {
171             e1.printStackTrace();
172         }
173 */
174         final String p = urmel.getFile();
175         final int position = p.lastIndexOf("/");
176         if (position >= && position + < p.length()) {
177             this.path = p.substring(0, position"/";
178             this.fileName = p.substring(position + 1);
179         else {
180             this.path = "";
181             this.fileName = p;
182         }
183         Trace.trace(CLASS, this, method, "path=" this.path);
184         Trace.trace(CLASS, this, method, "fileName=" this.fileName);
185         this.relativeAddress = !this.path.startsWith("/");
186         if (!this.fileName.endsWith(".xml")) {
187             throw new MalformedURLException("file name doesn't end with \".xml\": "
188                 this.fileName);
189         }
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(this.path + this.fileName);
194         if (positionPath < 0) {
195             throw new IllegalArgumentException(
196                 "couldn't determine begin of file path: "
197                 + url);
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     // FIXME m31 20100820: what if we have "hoho/hello/sample.xml" and "hoho/hello/../hello/sample.xml"
284     public final boolean equals(final Object object) {
285         if (object == null || !(object instanceof DefaultModuleAddress)) {
286             return false;
287         }
288         return url.equals(((DefaultModuleAddressobject).url);
289     }
290 
291     /**
292      * Get the file name of the specified module.
293      *
294      @param   spec    Here are the (perhaps relative) addresses to
295      *                  another module.
296      @return  File name of specified module.
297      */
298     private static final String getModuleFileName(final Specification spec) {
299 
300         return spec.getName() ".xml";
301     }
302 
303     public final ModuleAddress[] getModulePaths(final Specification specthrows IOException {
304 
305         final String fileNameEnd = getModuleFileName(spec);
306         final LocationList locations = spec.getLocationList();
307         final ModuleAddress[] result
308             new ModuleAddress[(locations != null ? locations.size() 0)];
309         for (int i = 0; i < result.length; i++) {
310             String file = locations.get(i).getLocation();
311             if (file.equals(".")) {
312                 file = "";
313             else if (!file.endsWith("/")) {
314                 file += "/";
315             }
316             file += fileNameEnd;
317             result[inew DefaultModuleAddress(file, this);
318         }
319         return result;
320     }
321 
322     /**
323      * Create relative address from <code>orgin</code> to <code>next</code>.
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         try {
335             final URL urlOrgin = new URL(origin);
336             final URL urlNext = new URL(next);
337 
338             if (urlOrgin.getProtocol().equals(urlNext.getProtocol())
339                     && urlOrgin.getHost().equals(urlNext.getHost())
340                     && urlOrgin.getPort() == urlNext.getPort()) {
341                 final String org = urlOrgin.getFile();
342                 final String nex = urlNext.getFile();
343                 int i = -1// position of next '/'
344                 int j = 0;  // position of last '/'
345                 while (<= (i = org.indexOf("/", j))) {
346                     if (i >= && nex.length() > i
347                             && org.substring(j, i).equals(
348                             nex.substring(j, i))) {
349                         j = i + 1;
350                     else {
351                         break;
352                     }
353                 }
354                 if (j > 0) {
355                     i = j;
356                     StringBuffer result = new StringBuffer(nex.length());
357                     while (<= (i = org.indexOf("/", i))) {
358                         i++;
359                         result.append("../");
360                     }
361                     result.append(nex.substring(j));
362                     return result.toString();
363                 else {
364                     return "/" + nex;
365                 }
366             else {    // no relative address possible
367                 return urlNext.toString();
368             }
369         catch (MalformedURLException e) {
370             return next;
371         }
372 
373     }
374 
375 
376 }