001 /*
002 * $Revision: 14596 $ $Date: 2009-01-19 08:12:38 +0100 (Mon, 19 Jan 2009) $ This file is part of M y C o R e See http://www.mycore.de/ for details. This program
003 * is free software; you can use it, redistribute it and / or modify it under the terms of the GNU General Public License (GPL) as published by the Free
004 * Software Foundation; either version 2 of the License or (at your option) any later version. This program is distributed in the hope that it will be useful,
005 * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
006 * more details. You should have received a copy of the GNU General Public License along with this program, in a file called gpl.txt or license.txt. If not,
007 * write to the Free Software Foundation Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307 USA
008 */
009
010 package org.mycore.datamodel.ifs;
011
012 import java.io.IOException;
013 import java.text.DateFormat;
014 import java.text.SimpleDateFormat;
015 import java.util.GregorianCalendar;
016
017 import org.jdom.Document;
018 import org.jdom.Element;
019 import org.jdom.JDOMException;
020 import org.mycore.common.MCRUsageException;
021
022 /**
023 * Represents a stored file or directory node with its metadata and content.
024 *
025 * @author Frank L\u00fctzenkirchen
026 * @version $Revision: 14596 $ $Date: 2009-01-19 08:12:38 +0100 (Mon, 19 Jan 2009) $
027 */
028 public abstract class MCRFilesystemNode {
029 protected static MCRFileMetadataManager manager = MCRFileMetadataManager.instance();
030
031 public static MCRFilesystemNode getNode(String ID) {
032 if (ID == null || ID.trim().length() == 0) {
033 throw new MCRUsageException("ID is an empty String or null");
034 }
035
036 return manager.retrieveNode(ID);
037 }
038
039 public static MCRFilesystemNode getRootNode(String ownerID) {
040 if (ownerID == null || ownerID.trim().length() == 0) {
041 throw new MCRUsageException("owner ID is an empty String or null");
042 }
043
044 return manager.retrieveRootNode(ownerID);
045 }
046
047 protected String ID;
048
049 /** The ID of the node owner, e .g. a MILESS derivate ID */
050 protected String ownerID;
051
052 /** The ID of the parent directory, if any */
053 protected String parentID;
054
055 /** The name of this node */
056 protected String name;
057
058 /** The optional label of this node */
059 protected String label;
060
061 /** The size in number of bytes */
062 protected long size;
063
064 /** The date of last modification of this node */
065 protected GregorianCalendar lastModified;
066
067 /** A flag indicating if this node is deleted and therefore invalid */
068 protected boolean deleted = false;
069
070 protected MCRFilesystemNode(String name, String ownerID) {
071 this(name, null, ownerID);
072 }
073
074 protected MCRFilesystemNode(String name, MCRDirectory parent) {
075 this(name, parent.ID, parent.ownerID);
076 }
077
078 protected MCRFilesystemNode(String name, MCRDirectory parent, boolean checkName) {
079 this(name, parent.ID, parent.ownerID, checkName);
080 }
081
082 private MCRFilesystemNode(String name, String parentID, String ownerID) {
083 this(name, parentID, ownerID, true);
084 }
085
086 private MCRFilesystemNode(String name, String parentID, String ownerID, boolean doExistCheck) {
087 if (ownerID == null || ownerID.trim().length() == 0) {
088 throw new MCRUsageException("owner ID is an empty String or null");
089 }
090
091 this.ID = manager.createNodeID();
092 this.parentID = parentID;
093 this.ownerID = ownerID;
094 this.size = 0;
095 this.lastModified = new GregorianCalendar();
096 this.label = null;
097 checkName(name, doExistCheck);
098 this.name = name;
099 }
100
101 protected MCRFilesystemNode(String ID, String parentID, String ownerID, String name, String label, long size, GregorianCalendar date) {
102 this.ID = ID;
103 this.parentID = parentID;
104 this.ownerID = ownerID;
105 this.name = name;
106 this.label = label;
107 this.size = size;
108 this.lastModified = date;
109 this.deleted = false;
110 }
111
112 protected void storeNew() {
113 manager.storeNode(this);
114
115 if (hasParent()) {
116 getParent().addChild(this);
117 }
118 }
119
120 public void delete() {
121 this.removeAllAdditionalData();
122 manager.deleteNode(ID);
123
124 if (parentID != null) {
125 getParent().removeChild(this);
126 }
127
128 this.ID = null;
129 this.ownerID = null;
130 this.name = null;
131 this.label = null;
132 this.size = 0;
133 this.lastModified = null;
134 this.parentID = null;
135 this.deleted = true;
136 }
137
138 /*
139 * protected void checkName(String name, boolean doExistCheck) {
140 * MCRArgumentChecker.ensureNotEmpty(name, "name");
141 *
142 * boolean error = (name.indexOf("/") + name.indexOf("\\")) != -2; String
143 * errorMsg =
144 * "Filesystem node name must not contain '\' or '/' characters: " + name;
145 * MCRArgumentChecker.ensureIsFalse(error, errorMsg);
146 *
147 * if (hasParent() && doExistCheck) { boolean exists =
148 * getParent().hasChild(name); String existsMsg =
149 * "A node with this name already exists: " + name;
150 * MCRArgumentChecker.ensureIsFalse(exists, existsMsg); } }
151 */
152
153 /**
154 * Changed method because of problems with update of files.
155 *
156 * @author Stefan Freitag
157 *
158 */
159 protected void checkName(String name, boolean doExistCheck) {
160
161 if (name == null)
162 throw new MCRUsageException(name + " is null.");
163
164 boolean error = (name.indexOf("/") + name.indexOf("\\")) != -2;
165 String errorMsg = "Filesystem node name must not contain '\' or '/' characters: " + name;
166 if (error)
167 throw new MCRUsageException(errorMsg);
168
169 if (hasParent() && doExistCheck) {
170 boolean exists = getParent().hasChild(name);
171 if (exists) {
172 getParent().getChild(name).delete();
173 System.out.println(name + " exists already, file was deleted...");
174 checkName(name, true);
175 }
176 }
177 }
178
179 public String getID() {
180 return ID;
181 }
182
183 /**
184 * Returns the ID of the owner of this node
185 *
186 * @return the ID of the owner of this node
187 */
188 public String getOwnerID() {
189 ensureNotDeleted();
190
191 return ownerID;
192 }
193
194 public String getParentID() {
195 return parentID;
196 }
197
198 public MCRDirectory getParent() {
199 ensureNotDeleted();
200
201 if (!hasParent()) {
202 return null;
203 }
204 return MCRDirectory.getDirectory(parentID);
205 }
206
207 public boolean hasParent() {
208 ensureNotDeleted();
209
210 return (parentID != null);
211 }
212
213 public MCRDirectory getRootDirectory() {
214 ensureNotDeleted();
215
216 if (hasParent()) {
217 return getParent().getRootDirectory();
218 } else if (this instanceof MCRDirectory) {
219 return (MCRDirectory) this;
220 } else {
221 return null;
222 }
223 }
224
225 protected void ensureNotDeleted() {
226 if (deleted) {
227 throw new MCRUsageException("Do not use this node, it is deleted");
228 }
229 }
230
231 /**
232 * Sets the name of this node
233 */
234 public void setName(String name) {
235 ensureNotDeleted();
236
237 if (this.name.equals(name)) {
238 return;
239 }
240
241 checkName(name, true);
242 this.name = name;
243 this.lastModified = new GregorianCalendar();
244
245 manager.storeNode(this);
246
247 if (parentID != null) {
248 getParent().touch();
249 }
250 }
251
252 /**
253 * Returns the name of this node
254 *
255 * @return the name of this node
256 */
257 public String getName() {
258 ensureNotDeleted();
259
260 return name;
261 }
262
263 /**
264 * Sets the label of this node
265 *
266 * @param label
267 * the label (may be null)
268 */
269 public void setLabel(String label) {
270 ensureNotDeleted();
271
272 if ((label != null) && (label.trim().length() == 0)) {
273 label = null;
274 }
275
276 if (this.label.equals(label)) {
277 return;
278 }
279
280 this.label = label;
281 this.lastModified = new GregorianCalendar();
282
283 manager.storeNode(this);
284
285 if (parentID != null) {
286 getParent().touch();
287 }
288 }
289
290 /**
291 * Returns the label of this node
292 *
293 * @return the label of this node, or null
294 */
295 public String getLabel() {
296 ensureNotDeleted();
297
298 return label;
299 }
300
301 public String getPath() {
302 ensureNotDeleted();
303
304 if (hasParent()) {
305 return getParent().getPath() + "/" + name;
306 }
307 return name;
308 }
309
310 public String getAbsolutePath() {
311 ensureNotDeleted();
312
313 if (hasParent()) {
314 String path = getParent().getAbsolutePath();
315
316 if (!path.endsWith("/")) {
317 path += "/";
318 }
319
320 return path + name;
321 }
322 return "/";
323 }
324
325 /**
326 * Returns the node size as number of bytes
327 */
328 public long getSize() {
329 ensureNotDeleted();
330
331 return size;
332 }
333
334 /**
335 * Returns the node size, formatted as a string
336 */
337 public String getSizeFormatted() {
338 ensureNotDeleted();
339
340 return getSizeFormatted(size);
341 }
342
343 /**
344 * Takes a file size in bytes and formats it as a string for output. For
345 * values < 5 KB the output format is for example "320 Byte". For values
346 * > 5 KB the output format is for example "6,8 KB". For values > 1 MB
347 * the output format is for example "3,45 MB".
348 */
349 public static String getSizeFormatted(long bytes) {
350 String sizeUnit;
351 String sizeText;
352 double sizeValue;
353
354 if (bytes >= (1024 * 1024)) // >= 1 MB
355 {
356 sizeUnit = "MB";
357 sizeValue = (double) (Math.round(bytes / 10485.76)) / 100;
358 } else if (bytes >= (5 * 1024)) // >= 5 KB
359 {
360 sizeUnit = "KB";
361 sizeValue = (double) (Math.round(bytes / 102.4)) / 10;
362 } else // < 5 KB
363 {
364 sizeUnit = "Byte";
365 sizeValue = bytes;
366 }
367
368 sizeText = String.valueOf(sizeValue).replace('.', ',');
369
370 if (sizeText.endsWith(",0")) {
371 sizeText = sizeText.substring(0, sizeText.length() - 2);
372 }
373
374 return sizeText + " " + sizeUnit;
375 }
376
377 /**
378 * Returns the time of last modification of this node
379 */
380 public GregorianCalendar getLastModified() {
381 ensureNotDeleted();
382
383 return lastModified;
384 }
385
386 /**
387 * Stores additional XML data for this node. The name of the data element is
388 * used as unique key for storing data. If data with this name already
389 * exists, it is overwritten.
390 *
391 * @param data
392 * the additional XML data to be saved
393 * @throws IOException
394 * if the XML data can not be retrieved
395 * @throws JDOMException
396 * if the XML data can not be parsed
397 */
398 public void setAdditionalData(Element data) throws IOException, JDOMException {
399 MCRFile dataFile = MCRFile.getRootFile(this.ID);
400 Document doc;
401 if (dataFile == null) {
402 String name = "MCRFilesystemNode.additionalData";
403 dataFile = new MCRFile(name, this.ID);
404 doc = new Document(new Element("additionalData"));
405 } else
406 doc = dataFile.getContentAsJDOM();
407
408 Element child = doc.getRootElement().getChild(data.getName());
409 if (child != null)
410 child.detach();
411 doc.getRootElement().addContent((Element) (data.clone()));
412 dataFile.setContentFrom(doc);
413 }
414
415 /**
416 * Removes additional XML data from this node.
417 *
418 * @param dataName
419 * the name of the additional XML data element to be removed
420 * @throws IOException
421 * if the XML data can not be retrieved
422 * @throws JDOMException
423 * if the XML data can not be parsed
424 */
425 public void removeAdditionalData(String dataName) throws IOException, JDOMException {
426 MCRFile dataFile = MCRFile.getRootFile(this.ID);
427 if (dataFile == null)
428 return;
429 Document doc = dataFile.getContentAsJDOM();
430 Element child = doc.getRootElement().getChild(dataName);
431 if (child != null)
432 child.detach();
433 if (doc.getRootElement().getChildren().size() == 0)
434 dataFile.delete();
435 else
436 dataFile.setContentFrom(doc);
437 }
438
439 /**
440 * Removes all additional XML data stored for this node, if any.
441 */
442 public void removeAllAdditionalData() {
443 MCRFile dataFile = MCRFile.getRootFile(this.ID);
444 if (dataFile != null)
445 dataFile.delete();
446 }
447
448 /**
449 * Gets additional XML data stored for this node, if any.
450 *
451 * @param dataName
452 * the name of the additional XML data element to be retrieved
453 * @return the additional XML data elemet that was stored, or null
454 * @throws IOException
455 * if the XML data can not be retrieved
456 * @throws JDOMException
457 * if the XML data can not be parsed
458 */
459 public Element getAdditionalData(String dataName) throws IOException, JDOMException {
460 MCRFile dataFile = MCRFile.getRootFile(this.ID);
461 if ((dataFile == null) || (dataFile.getSize() == 0))
462 return null;
463 Document doc = dataFile.getContentAsJDOM();
464 return doc.getRootElement().getChild(dataName);
465 }
466
467 /**
468 * Gets all additional XML data stored for this node, if any.
469 *
470 * @return the additional XML data document that was stored, or null
471 * @throws IOException
472 * if the XML data can not be retrieved
473 * @throws JDOMException
474 * if the XML data can not be parsed
475 */
476 public Document getAllAdditionalData() throws IOException, JDOMException {
477 MCRFile dataFile = MCRFile.getRootFile(this.ID);
478 if ((dataFile == null) || (dataFile.getSize() == 0))
479 return null;
480 else
481 return dataFile.getContentAsJDOM();
482 }
483
484 protected static DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss_SSS");
485
486 public String toString() {
487 String date = formatter.format(lastModified.getTime());
488
489 StringBuffer sb = new StringBuffer();
490 sb.append("ID = ").append(this.ID).append("\n");
491 sb.append("Name = ").append(this.name).append("\n");
492 sb.append("Label = ").append(this.label).append("\n");
493 sb.append("Type = ").append(this.getClass().getName()).append("\n");
494 sb.append("ParentID = ").append(this.parentID).append("\n");
495 sb.append("OwnerID = ").append(this.ownerID).append("\n");
496 sb.append("Size = ").append(this.size).append("\n");
497 sb.append("Modified = ").append(date).append("\n");
498
499 return sb.toString();
500 }
501 }