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 u) throws MalformedURLException {
069 this(u, (DefaultModuleAddress) null);
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 u) throws MalformedURLException {
081 this(u.toExternalForm(), (ModuleAddress) null);
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 file) throws 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 >= 0 && position + 1 < 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(((DefaultModuleAddress) object).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 spec) throws 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[i] = new 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 (0 <= (i = org.indexOf("/", j))) {
346 if (i >= 0 && 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 (0 <= (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 }
|