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 import java.util.ArrayList;
023 import java.util.List;
024
025 import org.qedeq.base.io.Path;
026 import org.qedeq.base.io.UrlUtility;
027 import org.qedeq.base.trace.Trace;
028 import org.qedeq.base.utility.StringUtility;
029 import org.qedeq.kernel.se.base.module.LocationList;
030 import org.qedeq.kernel.se.base.module.Specification;
031
032
033 /**
034 * An object of this class represents an address for a QEDEQ module.
035 *
036 * @author Michael Meyling
037 */
038 public class DefaultModuleAddress implements ModuleAddress {
039
040 /** Default memory module address with identifier "default". */
041 public static final DefaultModuleAddress MEMORY = new DefaultModuleAddress();
042
043 /** This class. */
044 private static final Class CLASS = DefaultModuleAddress.class;
045
046 /** URL form of this address. */
047 private final String url;
048
049 /** Header (including protocol, host, port, user) but without file path. */
050 private final String header;
051
052 /** Path (without protocol, host, port and file name). */
053 private final String path;
054
055 /** File name of QEDEQ module including <code>.xml</code>. */
056 private final String fileName;
057
058 /** Is module address relative? */
059 private final boolean relativeAddress;
060
061 /** Is module address a file? */
062 private final boolean fileAddress;
063
064 /** Module name. That is file name without <code>.xml</code> */
065 private final String name;
066
067 /**
068 * Constructor.
069 *
070 * @param u Address of module. Must not be <code>null</code>.
071 * Must be a URL with protocol "file" or "http" and address a file
072 * with extension ".xml".
073 * @throws MalformedURLException Address is formally incorrect.
074 */
075 public DefaultModuleAddress(final String u) throws MalformedURLException {
076 this(u, (DefaultModuleAddress) null);
077 }
078
079 /**
080 * Constructor.
081 *
082 * @param u Address of module. Must not be <code>null</code>.
083 * Must be a URL with protocol "file" or "http" and address a file
084 * with extension ".xml".
085 * @throws MalformedURLException Address is formally incorrect.
086 */
087 public DefaultModuleAddress(final URL u) throws MalformedURLException {
088 this(u.toExternalForm(), (ModuleAddress) null);
089 }
090
091 /**
092 * Constructor.
093 *
094 * @param file File path of module. Must address a file
095 * with extension ".xml".
096 * @throws IOException Problem with file location.
097 */
098 public DefaultModuleAddress(final File file) throws IOException {
099 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 }
|