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 /** Default memory module address with identifier "default". */
037 public static final DefaultModuleAddress MEMORY = new DefaultModuleAddress();
038
039 /** This class. */
040 private static final Class CLASS = DefaultModuleAddress.class;
041
042 /** URL form of this address. */
043 private final String url;
044
045 /** Header (including protocol, host, port, user) but without file path. */
046 private final String header;
047
048 /** Path (without protocol, host, port and file name). */
049 private final String path;
050
051 /** File name of QEDEQ module including <code>.xml</code>. */
052 private final String fileName;
053
054 /** Is module address relative? */
055 private final boolean relativeAddress;
056
057 /** Is module address a file? */
058 private final boolean fileAddress;
059
060 /** Module name. That is file name without <code>.xml</code> */
061 private final String name;
062
063 /**
064 * Constructor.
065 *
066 * @param u Address of module. Must not be <code>null</code>.
067 * Must be a URL with protocol "file" or "http" and address a file
068 * with extension ".xml".
069 * @throws MalformedURLException Address is formally incorrect.
070 */
071 public DefaultModuleAddress(final String u) throws MalformedURLException {
072 this(u, (DefaultModuleAddress) null);
073 }
074
075 /**
076 * Constructor.
077 *
078 * @param u Address of module. Must not be <code>null</code>.
079 * Must be a URL with protocol "file" or "http" and address a file
080 * with extension ".xml".
081 * @throws MalformedURLException Address is formally incorrect.
082 */
083 public DefaultModuleAddress(final URL u) throws MalformedURLException {
084 this(u.toExternalForm(), (ModuleAddress) null);
085 }
086
087 /**
088 * Constructor.
089 *
090 * @param file File path of module. Must address a file
091 * with extension ".xml".
092 * @throws IOException Problem with file location.
093 */
094 public DefaultModuleAddress(final File file) throws IOException {
095 this(IoUtility.toUrl(file.getCanonicalFile()));
096 }
097
098 /**
099 * Default constructor for memory modules.
100 */
101 public DefaultModuleAddress() {
102 this(true, "default");
103 }
104
105 /**
106 * Constructor mainly used for memory modules.
107 * TODO 20110227 m31: this is no object oriented design: a parameter must be true???
108 * refactor code and create extra memory module address class!
109 *
110 * @param memory Must be true. If not a runtime exception occurs.
111 * @param identifier Identifies the module in memory.
112 */
113 public DefaultModuleAddress(final boolean memory, final String identifier) {
114 if (!memory) {
115 throw new IllegalArgumentException("memory must be true");
116 }
117 url = "memory://" + identifier;
118 name = identifier;
119 fileAddress = false;
120 fileName = identifier;
121 header = "memory:";
122 path = "";
123 relativeAddress = true;
124 }
125
126 /**
127 * Constructor.
128 *
129 * @param address Address of module. Must not be <code>null</code>.
130 * Must be a URL with protocol "file" or "http" (if <code>parent</code> is
131 * <code>null</code>) and address a file
132 * with extension ".xml".
133 * @param parent Address of parent module. Can be <code>null</code>.
134 * @throws MalformedURLException Address is formally incorrect.
135 */
136 public DefaultModuleAddress(final String address, final ModuleAddress parent)
137 throws MalformedURLException {
138 final String method = "ModuleAddress(String, ModuleAddress)";
139 if (address == null) {
140 throw new NullPointerException();
141 }
142 URL urmel;
143 try {
144 if (parent != null) {
145 urmel = new URL(new URL(parent.getUrl()), address);
146 } else {
147 urmel = new URL(address);
148 }
149 } catch (MalformedURLException e) {
150 Trace.trace(CLASS, this, method, "address=" + address);
151 Trace.trace(CLASS, this, method, "parent=" + parent);
152 Trace.trace(CLASS, this, method, e);
153 try {
154 final String newAddress = "file:" + address;
155 if (parent != null) {
156 urmel = new URL(new URL(parent.getUrl()), newAddress);
157 } else {
158 urmel = new URL(newAddress);
159 }
160 } catch (MalformedURLException ex) {
161 throw e; // throw original exception
162 }
163 }
164 Trace.trace(CLASS, this, method, "protocol=" + urmel.getProtocol());
165 fileAddress = urmel.getProtocol().equalsIgnoreCase("file");
166 if (!fileAddress) {
167 url = urmel.toString();
168 } else {
169 String urm = urmel.toString();
170 if (urm.startsWith("file:") && !urm.startsWith("file://")) {
171 urm = "file://" + urm.substring("file:".length());
172 }
173 url = urm;
174 }
175 /*
176 Trace.trace(this, METHOD, "url.getFile=" + this.url.getFile());
177 Trace.trace(this, METHOD, "url.getPath=" + this.url.getPath());
178 try {
179 Trace.trace(this, METHOD, "URI File=" +
180 new File(new URI(this.address)).getAbsoluteFile());
181 } catch (URISyntaxException e1) {
182 e1.printStackTrace();
183 }
184 */
185 final String p = urmel.getFile();
186 final int position = p.lastIndexOf("/");
187 if (position >= 0 && position + 1 < p.length()) {
188 this.path = p.substring(0, position) + "/";
189 this.fileName = p.substring(position + 1);
190 } else {
191 this.path = "";
192 this.fileName = p;
193 }
194 Trace.trace(CLASS, this, method, "path=" + this.path);
195 Trace.trace(CLASS, this, method, "fileName=" + this.fileName);
196 this.relativeAddress = !this.path.startsWith("/");
197 if (!this.fileName.endsWith(".xml")) {
198 throw new MalformedURLException("file name doesn't end with \".xml\": "
199 + this.fileName);
200 }
201 final int positionBefore = this.fileName.lastIndexOf(".");
202 final String mname = this.fileName.substring(0, positionBefore);
203 this.name = mname;
204 final int positionPath = url.lastIndexOf(this.path + this.fileName);
205 if (positionPath < 0) {
206 throw new IllegalArgumentException(
207 "couldn't determine begin of file path: "
208 + url);
209 }
210 this.header = url.substring(0, positionPath);
211 }
212
213 /**
214 * Get module address as {@link ModuleContext}. Creates a new object.
215 *
216 * @return Module address as {@link ModuleContext}.
217 */
218 public ModuleContext createModuleContext() {
219 return new ModuleContext(this);
220 }
221
222 /**
223 * Get address header (including protocol, host, port, user)
224 * but without file path.
225 *
226 * @return address header
227 */
228 public String getHeader() {
229 return header;
230 }
231
232 /**
233 * Get address path (without protocol, host, port and file name).
234 *
235 * @return module path
236 */
237 public String getPath() {
238 return path;
239 }
240
241 /**
242 * Get module file name.
243 *
244 * @return Module file name.
245 */
246 public String getFileName() {
247 return fileName;
248 }
249
250 /**
251 * Get name of module (file name without <code>.xml</code>).
252 *
253 * @return Module name.
254 */
255 public String getName() {
256 return this.name;
257 }
258
259 /**
260 * Get fully qualified URL of module.
261 *
262 * @return URL for QEDEQ module.
263 */
264 public String getUrl() {
265 return this.url;
266 }
267
268 /**
269 * Was this module address created relatively?
270 *
271 * @return Relatively created?
272 */
273 public boolean isRelativeAddress() {
274 return this.relativeAddress;
275 }
276
277 /**
278 * Is this a local QEDEQ file. That means the address starts with <code>file:</code>.
279 *
280 * @return Is the QEDEQ module a local file?
281 */
282 public boolean isFileAddress() {
283 return fileAddress;
284 }
285
286 public final String toString() {
287 return url;
288 }
289
290 public final int hashCode() {
291 return url.hashCode();
292 }
293
294 // FIXME m31 20100820: what if we have "hoho/hello/sample.xml" and "hoho/hello/../hello/sample.xml"
295 public final boolean equals(final Object object) {
296 if (object == null || !(object instanceof DefaultModuleAddress)) {
297 return false;
298 }
299 return url.equals(((DefaultModuleAddress) object).url);
300 }
301
302 /**
303 * Get the file name of the specified module.
304 *
305 * @param spec Here are the (perhaps relative) addresses to
306 * another module.
307 * @return File name of specified module.
308 */
309 private static final String getModuleFileName(final Specification spec) {
310
311 return spec.getName() + ".xml";
312 }
313
314 public final ModuleAddress[] getModulePaths(final Specification spec) throws IOException {
315
316 final String fileNameEnd = getModuleFileName(spec);
317 final LocationList locations = spec.getLocationList();
318 final ModuleAddress[] result
319 = new ModuleAddress[(locations != null ? locations.size() : 0)];
320 for (int i = 0; i < result.length; i++) {
321 String file = locations.get(i).getLocation();
322 if (file.equals(".")) {
323 file = "";
324 } else if (!file.endsWith("/")) {
325 file += "/";
326 }
327 file += fileNameEnd;
328 result[i] = new DefaultModuleAddress(file, this);
329 }
330 return result;
331 }
332
333 /**
334 * Create relative address from <code>orgin</code> to <code>next</code>.
335 *
336 * @param origin This is the original location (URL!).
337 * @param next This should be the next location (URL!).
338 * @return Relative (or if necessary absolute) address.
339 */
340 public static final String createRelativeAddress(final String origin,
341 final String next) {
342 if (origin.equals(next)) {
343 return "";
344 }
345 try {
346 final URL urlOrgin = new URL(origin);
347 final URL urlNext = new URL(next);
348
349 if (urlOrgin.getProtocol().equals(urlNext.getProtocol())
350 && urlOrgin.getHost().equals(urlNext.getHost())
351 && urlOrgin.getPort() == urlNext.getPort()) {
352 final String org = urlOrgin.getFile();
353 final String nex = urlNext.getFile();
354 int i = -1; // position of next '/'
355 int j = 0; // position of last '/'
356 while (0 <= (i = org.indexOf("/", j))) {
357 if (i >= 0 && nex.length() > i
358 && org.substring(j, i).equals(
359 nex.substring(j, i))) {
360 j = i + 1;
361 } else {
362 break;
363 }
364 }
365 if (j > 0) {
366 i = j;
367 StringBuffer result = new StringBuffer(nex.length());
368 while (0 <= (i = org.indexOf("/", i))) {
369 i++;
370 result.append("../");
371 }
372 result.append(nex.substring(j));
373 return result.toString();
374 } else {
375 return "/" + nex;
376 }
377 } else { // no relative address possible
378 return urlNext.toString();
379 }
380 } catch (MalformedURLException e) {
381 return next;
382 }
383
384 }
385
386
387 }
|