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 u) throws MalformedURLException {
074 this(u, (DefaultModuleAddress) null);
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 u) throws MalformedURLException {
086 this(u.toExternalForm(), (ModuleAddress) null);
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 file) throws 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(((DefaultModuleAddress) object).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 spec) throws 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[i] = new 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 }
|