source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/util/GSDocumentModel.java@ 25128

Last change on this file since 25128 was 25074, checked in by sjm84, 12 years ago

Fixed a problem with deleting documents

  • Property svn:executable set to *
File size: 60.1 KB
Line 
1package org.greenstone.gsdl3.util;
2
3import java.io.BufferedWriter;
4import java.io.File;
5import java.io.FileInputStream;
6import java.io.FileOutputStream;
7import java.io.FileWriter;
8import java.io.IOException;
9import java.nio.channels.FileChannel;
10import java.util.ArrayList;
11import java.util.HashMap;
12import java.util.Vector;
13
14import javax.xml.parsers.DocumentBuilder;
15import javax.xml.parsers.DocumentBuilderFactory;
16import javax.xml.transform.Result;
17import javax.xml.transform.Transformer;
18import javax.xml.transform.TransformerFactory;
19import javax.xml.transform.dom.DOMSource;
20import javax.xml.transform.stream.StreamResult;
21
22import org.greenstone.gsdl3.core.MessageRouter;
23import org.w3c.dom.Document;
24import org.w3c.dom.Element;
25import org.w3c.dom.Node;
26import org.w3c.dom.NodeList;
27
28public class GSDocumentModel
29{
30 //The two archive databases
31 protected static final String ARCHIVEINFSRC = "archiveinf-src";
32 protected static final String ARCHIVEINFDOC = "archiveinf-doc";
33
34 //Set operations
35 public static final int OPERATION_REPLACE = 1;
36 public static final int OPERATION_INSERT_BEFORE = 2;
37 public static final int OPERATION_INSERT_AFTER = 3;
38 public static final int OPERATION_APPEND = 4;
39
40 //Duplicate and move operations
41 public static final int OPERATION_TYPE_DOC_TO_DOC = 1;
42 public static final int OPERATION_TYPE_DOC_TO_SEC = 2;
43 public static final int OPERATION_TYPE_SEC_TO_DOC = 3;
44 public static final int OPERATION_TYPE_SEC_TO_SEC = 4;
45
46 //Error codes
47 public static final int NO_ERROR = 0;
48 public static final int ERROR_OID_NOT_SPECIFIED = -1;
49 public static final int ERROR_COLLECTION_NOT_SPECIFIED = -2;
50 public static final int ERROR_SOURCE_DOCUMENT_OR_SECTION_DOES_NOT_EXIST = -3;
51 public static final int ERROR_DESTINATION_DOCUMENT_OR_SECTION_DOES_NOT_EXIST = -4;
52 public static final int ERROR_DESTINATION_DOCUMENT_OR_SECTION_ALREADY_EXISTS = -5;
53 public static final int ERROR_COULD_NOT_DUPLICATE = -6;
54 public static final int ERROR_COULD_NOT_MOVE = -7;
55 public static final int ERROR_DOC_XML_COULD_NOT_BE_CREATED = -8;
56 public static final int ERROR_EXCEPTION_CREATING_DOC_XML_FILE = -9;
57 public static final int ERROR_METADATA_NAME_NOT_SPECIFIED = -10;
58 public static final int ERROR_METADATA_VALUE_NOT_SPECIFIED = -11;
59 public static final int ERROR_COULD_NOT_RETRIEVE_DOC_XML = -12;
60 public static final int ERROR_COULD_NOT_WRITE_TO_DOC_XML = -13;
61 public static final int ERROR_COULD_NOT_RETRIEVE_SECTION = -14;
62 public static final int ERROR_COULD_NOT_OPEN_DATABASE = -15;
63 public static final int ERROR_DATA_NOT_FOUND_IN_DATABASE = -16;
64 public static final int ERROR_COULD_NOT_DELETE = -16;
65 public static final int ERROR_OID_INCORRECT_FORMAT = -17;
66 public static final int ERROR_INVALID_MERGE = -18;
67 public static final int ERROR_INVALID_METADATA_POSITION = -19;
68 public static final int ERROR_INVALID_SPLIT = -20;
69 public static final int ERROR_DESTINATION_OID_NOT_SPECIFIED = -21;
70 public static final HashMap<Integer, String> _errorMessageMap;
71
72 static
73 {
74 //Corresponding error messages
75 HashMap<Integer, String> errorMessageMap = new HashMap<Integer, String>();
76 errorMessageMap.put(ERROR_OID_NOT_SPECIFIED, "OID not specified");
77 errorMessageMap.put(ERROR_COLLECTION_NOT_SPECIFIED, "Collection not specified");
78 errorMessageMap.put(ERROR_SOURCE_DOCUMENT_OR_SECTION_DOES_NOT_EXIST, "The specified source document or section does not exist");
79 errorMessageMap.put(ERROR_DESTINATION_DOCUMENT_OR_SECTION_DOES_NOT_EXIST, "The specified destination document or section does not exist");
80 errorMessageMap.put(ERROR_DESTINATION_DOCUMENT_OR_SECTION_ALREADY_EXISTS, "The specified destination document or section already exists");
81 errorMessageMap.put(ERROR_COULD_NOT_DUPLICATE, "There was an error duplicating document or section");
82 errorMessageMap.put(ERROR_COULD_NOT_MOVE, "There was an error moving document or section");
83 errorMessageMap.put(ERROR_DOC_XML_COULD_NOT_BE_CREATED, "The doc.xml file already exists or could not be created");
84 errorMessageMap.put(ERROR_EXCEPTION_CREATING_DOC_XML_FILE, "There was an exception while creating the doc.xml file");
85 errorMessageMap.put(ERROR_METADATA_NAME_NOT_SPECIFIED, "The name of the requested metadata was not specified");
86 errorMessageMap.put(ERROR_METADATA_VALUE_NOT_SPECIFIED, "The new value for this metadata was not specified");
87 errorMessageMap.put(ERROR_COULD_NOT_RETRIEVE_DOC_XML, "Could not retrieve the necessary doc.xml file");
88 errorMessageMap.put(ERROR_COULD_NOT_WRITE_TO_DOC_XML, "There was an error writing to the doc.xml file");
89 errorMessageMap.put(ERROR_COULD_NOT_RETRIEVE_SECTION, "There was an error retrieving the specified section from the doc.xml file");
90 errorMessageMap.put(ERROR_COULD_NOT_OPEN_DATABASE, "There was an error opening the archive database");
91 errorMessageMap.put(ERROR_DATA_NOT_FOUND_IN_DATABASE, "The specified information could not be found in the database");
92 errorMessageMap.put(ERROR_COULD_NOT_DELETE, "There was an error deleting this document");
93 errorMessageMap.put(ERROR_OID_INCORRECT_FORMAT, "The given OID was not in the correct format");
94 errorMessageMap.put(ERROR_INVALID_MERGE, "Merge can only be performed on two sections of the same level or a section and it's parent. Also the destination section cannot have any child sections");
95 errorMessageMap.put(ERROR_INVALID_METADATA_POSITION, "There is no metadata at the given position");
96 errorMessageMap.put(ERROR_INVALID_SPLIT, "A split at the given location is not possible, either the section does not have text or the split point does not exist");
97 errorMessageMap.put(ERROR_DESTINATION_OID_NOT_SPECIFIED, "The destination OID was not specified");
98 _errorMessageMap = errorMessageMap;
99 }
100
101 protected int _errorStatus = NO_ERROR;
102
103 protected String _siteHome;
104 protected Document _mainDoc;
105 protected MessageRouter _router;
106 protected HashMap<String, Document> _docCache = new HashMap<String, Document>();
107
108 public GSDocumentModel(String siteHome, Document mainDocument, MessageRouter router)
109 {
110 _siteHome = siteHome;
111 _mainDoc = mainDocument;
112 _router = router;
113 }
114
115 /**
116 * Can be used to create a document or create a section of a document
117 *
118 * @param oid
119 * is the identifier of the document/section to create.
120 * @param collection
121 * is the collection we want to create the document/section in.
122 */
123 public void documentCreate(String oid, String collection, UserContext userContext)
124 {
125 _errorStatus = NO_ERROR;
126 //If the collection is not specified then we cannot continue
127 if (collection == null || collection.equals(""))
128 {
129 _errorStatus = ERROR_COLLECTION_NOT_SPECIFIED;
130 return;
131 }
132
133 //If the document does not have an OID specified then generate one
134 if (oid == null || oid.equals(""))
135 {
136 oid = generateOID();
137 }
138 else if (archiveCheckDocumentOrSectionExists(oid, collection, userContext))
139 {
140 _errorStatus = ERROR_DESTINATION_DOCUMENT_OR_SECTION_ALREADY_EXISTS;
141 return;
142 }
143
144 //Check if the OID is the OID for a section
145 boolean section = false;
146 if (oid.contains("."))
147 {
148 section = true;
149 }
150
151 if (!section)
152 {
153 File newDir = new File(_siteHome + File.separatorChar + "collect" + File.separatorChar + collection + File.separatorChar + "archives" + File.separatorChar + oid);
154
155 if (!newDir.exists())
156 {
157 newDir.mkdir();
158 }
159
160 HashMap<String, ArrayList<String>> entries = new HashMap<String, ArrayList<String>>();
161 ArrayList<String> values = new ArrayList<String>();
162 values.add(oid + "/doc.xml");
163 entries.put("doc-file", values);
164
165 //Write the new entry to the archive database
166 archiveWriteEntryToDatabase(oid, collection, entries, userContext);
167 if (_errorStatus != NO_ERROR)
168 {
169 return;
170 }
171
172 //Create a basic doc.xml file to go in the new folder
173 documentXMLCreateDocXML(oid, collection, userContext);
174 }
175 else
176 {
177 documentXMLCreateSection(oid, collection, userContext);
178 }
179 }
180
181 /**
182 * Can be used to delete a document or section
183 *
184 * @param oid
185 * is the identifier of the document/section to delete.
186 * @param collection
187 * is the collection to delete the document/section from.
188 */
189 public void documentDelete(String oid, String collection, UserContext userContext)
190 {
191 _errorStatus = NO_ERROR;
192 if (oid == null || oid.equals(""))
193 {
194 _errorStatus = ERROR_OID_NOT_SPECIFIED;
195 return;
196 }
197 else if (collection == null || collection.equals(""))
198 {
199 _errorStatus = ERROR_COLLECTION_NOT_SPECIFIED;
200 return;
201 }
202
203 if (!archiveCheckDocumentOrSectionExists(oid, collection, userContext))
204 {
205 _errorStatus = ERROR_SOURCE_DOCUMENT_OR_SECTION_DOES_NOT_EXIST;
206 return;
207 }
208
209 //Check if the OID is the OID for a section
210 boolean section = false;
211 if (oid.contains("."))
212 {
213 section = true;
214 }
215
216 if (!section)
217 {
218 String archivesFile = archiveGetDocumentFilePath(oid, collection, userContext);
219 String archivesFolder = archivesFile.substring(0, archivesFile.lastIndexOf(File.separator));
220 File dirToDelete = new File(archivesFolder);
221
222 if (!dirToDelete.exists() || !dirToDelete.isDirectory() || !deleteDirectory(dirToDelete))
223 {
224 _errorStatus = ERROR_COULD_NOT_DELETE;
225 return;
226 }
227
228 //Remove the entry from the archive database
229 archiveRemoveEntryFromDatabase(oid, collection, userContext);
230 }
231 else
232 {
233 documentXMLDeleteSection(oid, collection, userContext);
234 }
235 }
236
237 /**
238 * Can be used to copy a document or section from one place to another.
239 *
240 * @param oid
241 * is the identifier of the document/section that is to be
242 * copied.
243 * @param collection
244 * is the collection the source document resides in.
245 * @param newOID
246 * is the new identifier for the document/section (it cannot
247 * already exist).
248 * @param newCollection
249 * is the collection the new document/section will be copied to.
250 * If this is null then the collection parameter will be used
251 * instead.
252 */
253 public void documentMoveOrDuplicate(String oid, String collection, String newOID, String newCollection, int operation, boolean move, UserContext userContext)
254 {
255 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
256 {
257 return;
258 }
259
260 //If a new collection is not specified then assume the collection of the original document
261 if (newCollection == null || newCollection.equals(""))
262 {
263 newCollection = collection;
264 }
265
266 //Generate an OID for the duplicate if we are not given one
267 if (newOID == null || newOID.equals(""))
268 {
269 _errorStatus = ERROR_DESTINATION_OID_NOT_SPECIFIED;
270 return;
271 }
272
273 boolean requiresDatabaseEntry = false;
274 int operationType = getOperation(oid, newOID);
275 switch (operationType)
276 {
277 case OPERATION_TYPE_DOC_TO_DOC:
278 {
279 String archiveDir = archiveGetDocumentFilePath(oid, collection, userContext);
280 if (_errorStatus != NO_ERROR)
281 {
282 return;
283 }
284
285 //Remove doc.xml from the file name
286 archiveDir = archiveDir.substring(0, archiveDir.lastIndexOf(File.separator));
287 File dirToDuplicate = new File(archiveDir);
288
289 //This is possibly not the best way to name the new directory
290 File newDir = new File(archiveDir.substring(0, archiveDir.lastIndexOf(File.separator) + File.separator.length()) + newOID);
291
292 if (dirToDuplicate.exists() && dirToDuplicate.isDirectory() && !newDir.exists())
293 {
294 if (!copyDirectory(dirToDuplicate, newDir))
295 {
296 _errorStatus = ERROR_COULD_NOT_DUPLICATE;
297 return;
298 }
299 }
300 else
301 {
302 _errorStatus = ERROR_COULD_NOT_DUPLICATE;
303 return;
304 }
305
306 if (move)
307 {
308 deleteDirectory(dirToDuplicate);
309 }
310
311 requiresDatabaseEntry = true;
312 break;
313 }
314 case OPERATION_TYPE_DOC_TO_SEC:
315 {
316 Document originalDocument = getDocXML(oid, collection, userContext);
317 Element originalSection = getTopLevelSectionElement(originalDocument);
318
319 documentXMLCreateSection(newOID, newCollection, userContext);
320 if (_errorStatus != NO_ERROR)
321 {
322 return;
323 }
324
325 documentXMLSetSection(newOID, newCollection, originalSection, operation, userContext);
326
327 if (move)
328 {
329 String archiveDirStr = archiveGetDocumentFilePath(oid, collection, userContext);
330 if (_errorStatus != NO_ERROR)
331 {
332 return;
333 }
334
335 File archiveDir = new File(archiveDirStr);
336
337 if (archiveDir.exists() && archiveDir.isDirectory())
338 {
339 deleteDirectory(archiveDir);
340 }
341 }
342 break;
343 }
344 case OPERATION_TYPE_SEC_TO_DOC:
345 {
346 Document originalDocument = getDocXML(oid, collection, userContext);
347 Element originalSection = getSectionBySectionNumber(originalDocument, getSectionFromOID(oid));
348
349 documentCreate(newOID, newCollection, userContext);
350 if (_errorStatus != NO_ERROR)
351 {
352 return;
353 }
354
355 documentXMLCreateSection(newOID, newCollection, userContext);
356 if (_errorStatus != NO_ERROR)
357 {
358 return;
359 }
360
361 documentXMLSetSection(newOID, newCollection, originalSection, operation, userContext);
362
363 if (move)
364 {
365 originalDocument = getDocXML(oid, collection, userContext);
366 originalSection.getParentNode().removeChild(originalSection);
367
368 //Write the new change back into the file
369 if (!writeXMLFile(originalDocument, oid, collection, userContext))
370 {
371 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
372 return;
373 }
374 }
375
376 requiresDatabaseEntry = true;
377 break;
378 }
379 case OPERATION_TYPE_SEC_TO_SEC:
380 {
381 Document originalDocument = getDocXML(oid, collection, userContext);
382 Element originalSection = getSectionBySectionNumber(originalDocument, getSectionFromOID(oid));
383
384 if (operation == OPERATION_REPLACE)
385 {
386 documentXMLCreateSection(newOID, newCollection, userContext);
387 if (_errorStatus != NO_ERROR)
388 {
389 return;
390 }
391 }
392
393 documentXMLSetSection(newOID, newCollection, originalSection, operation, userContext);
394 if (_errorStatus != NO_ERROR)
395 {
396 return;
397 }
398
399 if (move)
400 {
401 originalDocument = getDocXML(oid, collection, userContext);
402 originalSection.getParentNode().removeChild(originalSection);
403
404 //Write the new change back into the file
405 if (!writeXMLFile(originalDocument, oid, collection, userContext))
406 {
407 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
408 return;
409 }
410 }
411
412 break;
413 }
414 }
415
416 if (requiresDatabaseEntry)
417 {
418 HashMap<String, ArrayList<String>> entries = new HashMap<String, ArrayList<String>>();
419 ArrayList<String> values = new ArrayList<String>();
420 values.add(newOID + "/doc.xml");
421 entries.put("doc-file", values);
422
423 //Write the new entry to the archive database
424 archiveWriteEntryToDatabase(newOID, newCollection, entries, userContext);
425 }
426 }
427
428 /**
429 * Can be used to acquire information about a given document or section.
430 *
431 * @param oid
432 * is the identifier of the document or section.
433 * @param collection
434 * is the collection the document or section resides in.
435 * @param requestedInfo
436 * is an array containing the various requests.
437 * @return This returns an array containing the requested information.
438 */
439 public String[] documentGetInformation(String oid, String collection, String[] requestedInfo, UserContext userContext)
440 {
441 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
442 {
443 return null;
444 }
445
446 for (int j = 0; j < requestedInfo.length; j++)
447 {
448 String currentRequest = requestedInfo[j];
449 //TODO: Decide what info requests are valid (e.g. number of sections etc.)
450 //-How many child/sibling sections
451 //-Metadata keys
452 }
453 //TODO: Implement
454 return null;
455 }
456
457 /**
458 * Can be used to merge two parts of a document together. The sections must
459 * be in the same document at the same level (e.g. D11.1.2 and D11.1.3) or a
460 * section and it's parent (e.g. D11.1.2 and D11.1). Also, the destination
461 * section cannot have any child sections.
462 *
463 * @param oid
464 * the identifier of the section that is to be merged.
465 * @param collection
466 * the collection the section resides in.
467 * @param mergeOID
468 * the identifier of the section that the source section will be
469 * merged into.
470 */
471 public void documentMerge(String oid, String collection, String mergeOID, UserContext userContext)
472 {
473 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
474 {
475 return;
476 }
477
478 if ((_errorStatus = checkOIDandCollection(mergeOID, collection, userContext)) != NO_ERROR)
479 {
480 return;
481 }
482
483 int op = getOperation(oid, mergeOID);
484 if (op != OPERATION_TYPE_SEC_TO_SEC && op != OPERATION_TYPE_SEC_TO_DOC) //We allow SEC_TO_DOC in the case that someone wants to merge D11.1 with D11 (for example)
485 {
486 _errorStatus = ERROR_INVALID_MERGE;
487 return;
488 }
489
490 String[] sourceLevels = oid.split("\\.");
491 String[] destinationLevels = mergeOID.split("\\.");
492
493 if (destinationLevels.length > sourceLevels.length)
494 {
495 _errorStatus = ERROR_INVALID_MERGE;
496 return;
497 }
498
499 for (int i = 0; i < sourceLevels.length - 1; i++)
500 {
501 if (i >= destinationLevels.length || !sourceLevels[i].equals(destinationLevels[i]))
502 {
503 _errorStatus = ERROR_INVALID_MERGE;
504 return;
505 }
506 }
507
508 Document docXML = getDocXML(oid, collection, userContext);
509 if (docXML == null)
510 {
511 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
512 return;
513 }
514
515 Element sourceSection = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
516 Element destinationSection = getSectionBySectionNumber(docXML, getSectionFromOID(mergeOID));
517
518 //Make sure the destination Section does not have any child Sections.
519 NodeList childSections = GSXML.getChildrenByTagName(destinationSection, GSXML.DOCXML_SECTION_ELEM);
520 if (childSections.getLength() != 0 && sourceLevels.length == destinationLevels.length)
521 {
522 _errorStatus = ERROR_INVALID_MERGE;
523 return;
524 }
525
526 //Get the children of the destination section so we can copy them to the source section before we overwrite the destination
527 NodeList childrenToKeep = destinationSection.getChildNodes();
528 ArrayList<Node> childList = new ArrayList<Node>();
529 for (int i = 0; i < childrenToKeep.getLength(); i++)
530 {
531 //Need to put these in a list to make them easier to loop through as using a NodeList is messy
532 childList.add(childrenToKeep.item(i));
533 }
534
535 //for(int i = 0; i < childrenToKeep.getLength(); i++)
536 for (int i = 0; i < childList.size(); i++)
537 {
538 Node currentChild = childList.get(i);
539 //If the child is not a <Content> node then add it to source section
540 if (!currentChild.getNodeName().equals(GSXML.DOCXML_CONTENT_ELEM))
541 {
542 sourceSection.appendChild(currentChild);
543 continue;
544 }
545
546 //Get the destination Section's Content node's text node, if it's empty then we don't need to worry about appending it
547 Node destinationTextNode = currentChild.getFirstChild();
548 if (destinationTextNode == null || destinationTextNode.getNodeValue() == null || destinationTextNode.getNodeValue().equals(""))
549 {
550 continue;
551 }
552
553 //If the source Section does not have a content then directly append the destination content
554 Element sourceContent = (Element) GSXML.getChildByTagName(sourceSection, GSXML.DOCXML_CONTENT_ELEM);
555 if (sourceContent == null)
556 {
557 sourceSection.appendChild(currentChild);
558 continue;
559 }
560
561 //If the source Section's Content is empty then again we can directly append the destination content
562 Node sourceTextNode = sourceContent.getFirstChild();
563 if (sourceTextNode == null || sourceTextNode.getNodeValue() == null || sourceTextNode.getNodeValue().equals(""))
564 {
565 sourceSection.appendChild(currentChild);
566 continue;
567 }
568
569 //Otherwise, set the new content to be destination + source text in that order.
570 sourceTextNode.setNodeValue(destinationTextNode.getNodeValue() + " " + sourceTextNode.getNodeValue());
571 }
572
573 documentXMLSetSection(mergeOID, collection, sourceSection, OPERATION_REPLACE, userContext);
574 if (_errorStatus != NO_ERROR)
575 {
576 return;
577 }
578
579 documentXMLDeleteSection(oid, collection, userContext);
580 }
581
582 /**
583 * Can be used to split a section into two sections (e.g. D11.2.1 will
584 * become D11.2.1 and D11.2.2). Any child section will belong to the second
585 * section (D11.2.2 in the example).
586 *
587 * @param oid
588 * is the identifer of the section to be split.
589 * @param collection
590 * is the collection the section resides in.
591 * @param splitPoint
592 * is the point in the text we want to split at.
593 */
594 public void documentSplit(String oid, String collection, int splitPoint, UserContext userContext)
595 {
596 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
597 {
598 return;
599 }
600
601 Document docXML = getDocXML(oid, collection, userContext);
602 if (docXML == null)
603 {
604 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
605 return;
606 }
607
608 Element sectionToSplit = null;
609 if (!oid.contains("."))
610 {
611 sectionToSplit = getTopLevelSectionElement(docXML);
612 }
613 else
614 {
615 sectionToSplit = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
616 }
617
618 Element content = (Element) GSXML.getChildByTagName(sectionToSplit, GSXML.DOCXML_CONTENT_ELEM);
619 if (content == null)
620 {
621 _errorStatus = ERROR_INVALID_SPLIT;
622 return;
623 }
624
625 Node textNode = content.getFirstChild();
626 if (textNode == null)
627 {
628 _errorStatus = ERROR_INVALID_SPLIT;
629 return;
630 }
631
632 String text = textNode.getNodeValue();
633 if (splitPoint > text.length() - 2 || splitPoint < 1) //-1 would be the index of the last character, so the last valid split point is -2
634 {
635 _errorStatus = ERROR_INVALID_SPLIT;
636 return;
637 }
638
639 String firstPart = text.substring(0, splitPoint);
640 String secondPart = text.substring(splitPoint);
641
642 Element newSection = docXML.createElement(GSXML.DOCXML_SECTION_ELEM);
643 Element newContent = docXML.createElement(GSXML.DOCXML_CONTENT_ELEM);
644 Node newTextNode = docXML.createTextNode(firstPart);
645 newContent.appendChild(newTextNode);
646 newSection.appendChild(newContent);
647
648 documentXMLSetSection(oid, collection, newSection, OPERATION_INSERT_BEFORE, userContext);
649 if (_errorStatus != NO_ERROR)
650 {
651 return;
652 }
653 textNode.setNodeValue(secondPart);
654
655 //Write the new change back into the file
656 if (!writeXMLFile(docXML, oid, collection, userContext))
657 {
658 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
659 }
660 }
661
662 /**
663 * Creates a basic doc.xml file with minimal contents.
664 *
665 * @param oid
666 * is the identifier of the document to be created.
667 * @param collection
668 * is the collection the new document will reside in.
669 */
670 public void documentXMLCreateDocXML(String oid, String collection, UserContext userContext)
671 {
672 _errorStatus = NO_ERROR;
673 try
674 {
675 Document docXML = null;
676
677 String filePath = archiveGetDocumentFilePath(oid, collection, userContext);
678 File docFile = new File(filePath);
679 if (!docFile.exists() && !docFile.createNewFile())
680 {
681 _errorStatus = ERROR_DOC_XML_COULD_NOT_BE_CREATED;
682 return;
683 }
684
685 BufferedWriter bw = new BufferedWriter(new FileWriter(docFile));
686 bw.write("<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>\n");
687 bw.write("<!DOCTYPE Archive SYSTEM \"http://greenstone.org/dtd/Archive/1.0/Archive.dtd\">\n");
688 bw.write("<Archive>\n");
689 bw.write("</Archive>\n");
690 bw.close();
691
692 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
693 DocumentBuilder db = dbf.newDocumentBuilder();
694 docXML = db.parse(docFile);
695
696 _docCache.put(oid + "__" + collection, docXML);
697 }
698 catch (Exception ex)
699 {
700 ex.printStackTrace();
701 _errorStatus = ERROR_EXCEPTION_CREATING_DOC_XML_FILE;
702 return;
703 }
704 }
705
706 /**
707 * Gets a metadata value from a document or section.
708 *
709 * @param oid
710 * is the identifier of the section or document to get metadata
711 * from.
712 * @param collection
713 * is the collection the section or document resides in.
714 * @param metadataName
715 * is the name of metadata to retrieve
716 * @return an array of metadata elements containing the resquested metadata
717 */
718 public ArrayList<Element> documentXMLGetMetadata(String oid, String collection, String metadataName, UserContext userContext)
719 {
720 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
721 {
722 return null;
723 }
724 else if (metadataName == null || metadataName.equals(""))
725 {
726 _errorStatus = ERROR_METADATA_NAME_NOT_SPECIFIED;
727 return null;
728 }
729
730 Document docXML = getDocXML(oid, collection, userContext);
731 if (docXML == null)
732 {
733 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
734 return null;
735 }
736
737 return getMetadataElementsFromSection(docXML, oid, metadataName);
738 }
739
740 /**
741 *
742 * @param oid
743 * is the identifier of the document or section that is to have
744 * it's metadata set.
745 * @param collection
746 * is the collection the document/section resides in.
747 * @param metadataName
748 * is the name of the metadata value that is to be set.
749 * @param newMetadataValue
750 * is the new value of the metadata.
751 * @param position
752 * specifies the position of the value to set.
753 * @param operation
754 * can be one of OPERATION_REPLACE, OPERATION_INSERT_BEFORE,
755 * OPERATION_INSERT_AFTER or OPERATION_APPEND.
756 */
757 public void documentXMLSetMetadata(String oid, String collection, String metadataName, String newMetadataValue, int position, int operation, UserContext userContext)
758 {
759 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
760 {
761 return;
762 }
763 else if (metadataName == null || metadataName.equals(""))
764 {
765 _errorStatus = ERROR_METADATA_NAME_NOT_SPECIFIED;
766 return;
767 }
768 else if (newMetadataValue == null || newMetadataValue.equals(""))
769 {
770 _errorStatus = ERROR_METADATA_VALUE_NOT_SPECIFIED;
771 return;
772 }
773
774 Document docXML = getDocXML(oid, collection, userContext);
775 if (docXML == null)
776 {
777 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
778 return;
779 }
780
781 ArrayList<Element> metadataElems = getMetadataElementsFromSection(docXML, oid, metadataName);
782
783 if (operation != OPERATION_APPEND && metadataElems.get(position) == null)
784 {
785 _errorStatus = ERROR_INVALID_METADATA_POSITION;
786 return;
787 }
788
789 if (operation == OPERATION_REPLACE)
790 {
791 metadataElems.get(position).setNodeValue(newMetadataValue);
792 }
793 else if (operation == OPERATION_INSERT_BEFORE)
794 {
795 Element newMetadata = createElementWithValue(docXML, GSXML.DOCXML_METADATA_ELEM, metadataName, newMetadataValue);
796 Element existingMetadata = metadataElems.get(position);
797
798 existingMetadata.getParentNode().insertBefore(newMetadata, existingMetadata);
799 }
800 else if (operation == OPERATION_INSERT_AFTER)
801 {
802 Element newMetadata = createElementWithValue(docXML, GSXML.DOCXML_METADATA_ELEM, metadataName, newMetadataValue);
803 Element existingMetadata = metadataElems.get(position + 1);
804
805 if (existingMetadata != null)
806 {
807 existingMetadata.getParentNode().insertBefore(newMetadata, existingMetadata);
808 }
809 else
810 {
811 existingMetadata = metadataElems.get(position);
812 existingMetadata.getParentNode().appendChild(newMetadata);
813 }
814 }
815 else
816 {
817 Element section = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
818 Element description = (Element) GSXML.getChildByTagName(section, GSXML.DOCXML_DESCRIPTION_ELEM);
819 if (description == null)
820 {
821 description = docXML.createElement(GSXML.DOCXML_DESCRIPTION_ELEM);
822 section.appendChild(description);
823 }
824 Element newMetadata = createElementWithValue(docXML, GSXML.DOCXML_METADATA_ELEM, metadataName, newMetadataValue);
825 description.appendChild(newMetadata);
826 }
827
828 //Write the new change back into the file
829 if (!writeXMLFile(docXML, oid, collection, userContext))
830 {
831 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
832 }
833 }
834
835 /**
836 * Can be used to delete metadata at a specific position in a document or
837 * section (such as the third author of a document).
838 *
839 * @param oid
840 * is the identifier of the document/section to delete metadata
841 * from.
842 * @param collection
843 * is the collection the document resides in.
844 * @param metadataName
845 * is the name of the metadata that is to have an item deleted.
846 * @param position
847 * is position of the item that is to be deleted.
848 */
849 public void documentXMLDeleteMetadata(String oid, String collection, String metadataName, int position, UserContext userContext)
850 {
851 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
852 {
853 return;
854 }
855 else if (metadataName == null || metadataName.equals(""))
856 {
857 _errorStatus = ERROR_METADATA_NAME_NOT_SPECIFIED;
858 return;
859 }
860
861 Document docXML = getDocXML(oid, collection, userContext);
862 if (docXML == null)
863 {
864 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
865 return;
866 }
867
868 ArrayList<Element> metadataElems = getMetadataElementsFromSection(docXML, oid, metadataName);
869
870 if (metadataElems.get(position) != null)
871 {
872 metadataElems.get(position).getParentNode().removeChild(metadataElems.get(position));
873 }
874 }
875
876 /**
877 * Can be used to delete all the metadata with a specific name from a
878 * document or section (e.g. all of the authors).
879 *
880 * @param oid
881 * is the identifier of the document or section to delete the
882 * metadata from.
883 * @param collection
884 * is the collection the document resides in.
885 * @param metadataName
886 * is the name of the metadata to delete.
887 */
888 public void documentXMLDeleteMetadata(String oid, String collection, String metadataName, UserContext userContext)
889 {
890 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
891 {
892 return;
893 }
894 else if (metadataName == null || metadataName.equals(""))
895 {
896 _errorStatus = ERROR_METADATA_NAME_NOT_SPECIFIED;
897 return;
898 }
899
900 Document docXML = getDocXML(oid, collection, userContext);
901 if (docXML == null)
902 {
903 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
904 return;
905 }
906
907 ArrayList<Element> metadataElems = getMetadataElementsFromSection(docXML, oid, metadataName);
908
909 for (Element elem : metadataElems)
910 {
911 elem.getParentNode().removeChild(elem);
912 }
913 }
914
915 /**
916 * Can be used to replace a specific metadata item of a given name, given
917 * it's value.
918 *
919 * @param oid
920 * is the document/section of the metadata that is to be
921 * replaced.
922 * @param collection
923 * is the collection the document resides in.
924 * @param metadataName
925 * is the name of the metadata to be replaced.
926 * @param oldMetadataValue
927 * is the old value of the metadata (the value that will be
928 * replaced).
929 * @param newMetadataValue
930 * is the new value of the metadata that will replace the old
931 * value.
932 */
933 public void documentXMLReplaceMetadata(String oid, String collection, String metadataName, String oldMetadataValue, String newMetadataValue, UserContext userContext)
934 {
935 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
936 {
937 return;
938 }
939 else if (metadataName == null || metadataName.equals(""))
940 {
941 _errorStatus = ERROR_METADATA_NAME_NOT_SPECIFIED;
942 return;
943 }
944 else if (newMetadataValue == null || newMetadataValue.equals(""))
945 {
946 _errorStatus = ERROR_METADATA_VALUE_NOT_SPECIFIED;
947 return;
948 }
949 else if (oldMetadataValue == null || oldMetadataValue.equals(""))
950 {
951 _errorStatus = ERROR_METADATA_VALUE_NOT_SPECIFIED;
952 return;
953 }
954
955 Document docXML = getDocXML(oid, collection, userContext);
956 if (docXML == null)
957 {
958 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
959 return;
960 }
961
962 ArrayList<Element> metadataElems = getMetadataElementsFromSection(docXML, oid, metadataName);
963
964 for (Element elem : metadataElems)
965 {
966 Node textNode = elem.getFirstChild();
967
968 if (textNode != null && textNode.getNodeValue().equals(oldMetadataValue))
969 {
970 textNode.setNodeValue(newMetadataValue);
971 }
972 }
973
974 //Write the new change back into the file
975 if (!writeXMLFile(docXML, oid, collection, userContext))
976 {
977 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
978 }
979 }
980
981 /**
982 * Can be used to create a blank section.
983 *
984 * @param oid
985 * is the identifier of the section to be created.
986 * @param collection
987 * is the collection the document resides in.
988 */
989 public void documentXMLCreateSection(String oid, String collection, UserContext userContext)
990 {
991 _errorStatus = NO_ERROR;
992 if (oid == null || oid.equals(""))
993 {
994 _errorStatus = ERROR_OID_NOT_SPECIFIED;
995 return;
996 }
997 else if (collection == null || collection.equals(""))
998 {
999 _errorStatus = ERROR_COLLECTION_NOT_SPECIFIED;
1000 return;
1001 }
1002
1003 if (oid.contains(".") && !archiveCheckDocumentOrSectionExists(oid.substring(0, oid.indexOf(".")), collection, userContext))
1004 {
1005 documentCreate(oid.substring(0, oid.indexOf(".")), collection, userContext);
1006 if (_errorStatus != NO_ERROR)
1007 {
1008 return;
1009 }
1010 }
1011
1012 Document docXML = getDocXML(oid, collection, userContext);
1013 if (docXML == null)
1014 {
1015 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
1016 return;
1017 }
1018
1019 Element topLevel = docXML.getDocumentElement();
1020 if (!oid.contains("."))
1021 {
1022 if (GSXML.getChildByTagName(topLevel, GSXML.DOCXML_SECTION_ELEM) == null)
1023 {
1024 Element newSection = docXML.createElement(GSXML.DOCXML_SECTION_ELEM);
1025 topLevel.appendChild(newSection);
1026 }
1027 }
1028 else
1029 {
1030 int[] intLevels = null;
1031 try
1032 {
1033 intLevels = oidToSectionNumberArray(oid);
1034 }
1035 catch (Exception ex)
1036 {
1037 _errorStatus = ERROR_OID_INCORRECT_FORMAT;
1038 return;
1039 }
1040
1041 Element current = (Element) GSXML.getChildByTagName(topLevel, GSXML.DOCXML_SECTION_ELEM);
1042 if (current == null)
1043 {
1044 Element topLevelSection = docXML.createElement(GSXML.DOCXML_SECTION_ELEM);
1045 topLevel.appendChild(topLevelSection);
1046 current = topLevelSection;
1047 }
1048
1049 for (int currentLevelValue : intLevels)
1050 {
1051 NodeList sections = GSXML.getChildrenByTagName(current, GSXML.DOCXML_SECTION_ELEM);
1052
1053 if (sections.item(currentLevelValue - 1) == null)
1054 {
1055 Element latest = null;
1056 for (int j = 0; j < currentLevelValue - sections.getLength(); j++)
1057 {
1058 Element blankSection = docXML.createElement(GSXML.DOCXML_SECTION_ELEM);
1059 current.appendChild(blankSection);
1060 latest = blankSection;
1061 }
1062 current = latest;
1063 }
1064 else
1065 {
1066 current = (Element) sections.item(currentLevelValue - 1);
1067 }
1068 }
1069 }
1070
1071 //Write the new change back into the file
1072 if (!writeXMLFile(docXML, oid, collection, userContext))
1073 {
1074 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
1075 }
1076 }
1077
1078 /**
1079 * Can be used to delete an entire section.
1080 *
1081 * @param oid
1082 * is the identifier of the section to be deleted.
1083 * @param collection
1084 * is the collection the document resides in.
1085 */
1086 public void documentXMLDeleteSection(String oid, String collection, UserContext userContext)
1087 {
1088 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
1089 {
1090 return;
1091 }
1092
1093 Document docXML = getDocXML(oid, collection, userContext);
1094 if (docXML == null)
1095 {
1096 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
1097 return;
1098 }
1099
1100 Element section = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
1101 if (section == null)
1102 {
1103 _errorStatus = ERROR_COULD_NOT_DELETE;
1104 return;
1105 }
1106
1107 section.getParentNode().removeChild(section);
1108
1109 //Write the new change back into the file
1110 if (!writeXMLFile(docXML, oid, collection, userContext))
1111 {
1112 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
1113 }
1114 }
1115
1116 /**
1117 * Can be used to get a section from a document.
1118 *
1119 * @param oid
1120 * is the identifier of the section to get.
1121 * @param collection
1122 * is the collection the document resides in.
1123 * @return the requested section.
1124 */
1125 public Element documentXMLGetSection(String oid, String collection, UserContext userContext)
1126 {
1127 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
1128 {
1129 return null;
1130 }
1131
1132 Document docXML = getDocXML(oid, collection, userContext);
1133 if (docXML == null)
1134 {
1135 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
1136 return null;
1137 }
1138
1139 Element section = null;
1140 if (!oid.contains("."))
1141 {
1142 section = getTopLevelSectionElement(docXML);
1143 }
1144 else
1145 {
1146 section = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
1147 }
1148
1149 if (section == null)
1150 {
1151 _errorStatus = ERROR_COULD_NOT_RETRIEVE_SECTION;
1152 return null;
1153 }
1154
1155 return section;
1156 }
1157
1158 /**
1159 * Can be used to set an OID to the given section element.
1160 *
1161 * @param oid
1162 * is the identifier of the section to be set.
1163 * @param collection
1164 * is the collection the section will reside in.
1165 * @param newSection
1166 * is the new section element.
1167 * @param operation
1168 * can be one of OPERATION_REPLACE, OPERATION_INSERT_BEFORE,
1169 * OPERATION_INSERT_AFTER or OPERATION_APPEND.
1170 * @throws IOException
1171 */
1172 public void documentXMLSetSection(String oid, String collection, Element newSection, int operation, UserContext userContext)
1173 {
1174 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
1175 {
1176 return;
1177 }
1178
1179 Document docXML = getDocXML(oid, collection, userContext);
1180 if (docXML == null)
1181 {
1182 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
1183 return;
1184 }
1185
1186 Element existingSection = null;
1187 if (!oid.contains("."))
1188 {
1189 existingSection = getTopLevelSectionElement(docXML);
1190 }
1191 else
1192 {
1193 existingSection = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
1194 }
1195
1196 if (existingSection == null)
1197 {
1198 _errorStatus = ERROR_COULD_NOT_RETRIEVE_SECTION;
1199 return;
1200 }
1201
1202 Element importedSection = (Element) docXML.importNode(newSection.cloneNode(true), true);
1203 Node sectionParent = existingSection.getParentNode();
1204
1205 if (operation == OPERATION_APPEND)
1206 {
1207 existingSection.appendChild(importedSection);
1208 }
1209 else
1210 {
1211 //Remove the attributes that are only there to help us find the section
1212 importedSection.removeAttribute(GSXML.NODE_ID_ATT);
1213 importedSection.removeAttribute(GSXML.COLLECTION_ATT);
1214
1215 if (operation == OPERATION_INSERT_BEFORE || operation == OPERATION_REPLACE)
1216 {
1217 sectionParent.insertBefore(importedSection, existingSection);
1218 }
1219 else if (operation == OPERATION_INSERT_AFTER)
1220 {
1221 Node siblingNode = existingSection.getNextSibling();
1222 while (siblingNode != null && siblingNode.getNodeType() != Node.ELEMENT_NODE)
1223 {
1224 siblingNode = siblingNode.getNextSibling();
1225 }
1226
1227 if (siblingNode != null)
1228 {
1229 sectionParent.insertBefore(importedSection, siblingNode);
1230 }
1231 else
1232 {
1233 sectionParent.appendChild(importedSection);
1234 }
1235 }
1236
1237 if (operation == OPERATION_REPLACE)
1238 {
1239 sectionParent.removeChild(existingSection);
1240 }
1241 }
1242
1243 //Write the new change back into the file
1244 if (!writeXMLFile(docXML, oid, collection, userContext))
1245 {
1246 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
1247 return;
1248 }
1249 }
1250
1251 /**
1252 * Gets the text of a given section as a string.
1253 *
1254 * @param oid
1255 * is the identifier of the section to get the text from.
1256 * @param collection
1257 * is the collection the document resides in.
1258 * @return the text from the section.
1259 */
1260 public String documentXMLGetText(String oid, String collection, UserContext userContext)
1261 {
1262 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
1263 {
1264 return null;
1265 }
1266
1267 Document docXML = getDocXML(oid, collection, userContext);
1268 if (docXML == null)
1269 {
1270 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
1271 return null;
1272 }
1273
1274 Element section = null;
1275 if (!oid.contains("."))
1276 {
1277 section = getTopLevelSectionElement(docXML);
1278 }
1279 else
1280 {
1281 section = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
1282 }
1283
1284 if (section == null)
1285 {
1286 _errorStatus = ERROR_COULD_NOT_RETRIEVE_SECTION;
1287 return null;
1288 }
1289
1290 Element contentNode = (Element) GSXML.getChildByTagName(section, GSXML.DOCXML_CONTENT_ELEM);
1291 if (contentNode == null)
1292 {
1293 return null;
1294 }
1295
1296 Node textNode = contentNode.getFirstChild();
1297 if (textNode == null)
1298 {
1299 return null;
1300 }
1301
1302 return textNode.getNodeValue();
1303 }
1304
1305 /**
1306 * Sets the text of a given section using an element.
1307 *
1308 * @param oid
1309 * is the identifier of the section to set the text of.
1310 * @param collection
1311 * is the collection the document resides in.
1312 * @param newContent
1313 * is the new content element for the section.
1314 */
1315 public void documentXMLSetText(String oid, String collection, Element newContent, UserContext userContext)
1316 {
1317 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
1318 {
1319 return;
1320 }
1321
1322 Document docXML = getDocXML(oid, collection, userContext);
1323 if (docXML == null)
1324 {
1325 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
1326 return;
1327 }
1328
1329 Element section = null;
1330 if (!oid.contains("."))
1331 {
1332 section = getTopLevelSectionElement(docXML);
1333 }
1334 else
1335 {
1336 section = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
1337 }
1338
1339 if (section == null)
1340 {
1341 _errorStatus = ERROR_COULD_NOT_RETRIEVE_SECTION;
1342 return;
1343 }
1344
1345 Element existingContent = (Element) GSXML.getChildByTagName(section, GSXML.DOCXML_CONTENT_ELEM);
1346
1347 //Remove the attributes that are only there to help us find the content
1348 newContent.removeAttribute(GSXML.NODE_ID_ATT);
1349 newContent.removeAttribute(GSXML.COLLECTION_ATT);
1350
1351 Element importedContent = (Element) docXML.importNode(newContent, true);
1352 //If the current section does not have content then just add it, otherwise replace the existing one
1353 if (existingContent == null)
1354 {
1355 section.appendChild(importedContent);
1356 }
1357 else
1358 {
1359 Node contentParent = existingContent.getParentNode();
1360
1361 //Replace the old node in the document tree
1362 contentParent.insertBefore(importedContent, existingContent);
1363 contentParent.removeChild(existingContent);
1364 }
1365
1366 //Write the new change back into the file
1367 if (!writeXMLFile(docXML, oid, collection, userContext))
1368 {
1369 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
1370 return;
1371 }
1372 }
1373
1374 /**
1375 * Sets the text of a given section using a string.
1376 *
1377 * @param oid
1378 * is the identifier of the section to set the text of.
1379 * @param collection
1380 * is the collection the document resides in.
1381 * @param newContent
1382 * is the new text for the section.
1383 */
1384 public void documentXMLSetText(String oid, String collection, String newContent, UserContext userContext)
1385 {
1386 if ((_errorStatus = checkOIDandCollection(oid, collection, userContext)) != NO_ERROR)
1387 {
1388 return;
1389 }
1390
1391 Document docXML = getDocXML(oid, collection, userContext);
1392 if (docXML == null)
1393 {
1394 _errorStatus = ERROR_COULD_NOT_RETRIEVE_DOC_XML;
1395 return;
1396 }
1397
1398 Element section = null;
1399 if (!oid.contains("."))
1400 {
1401 section = getTopLevelSectionElement(docXML);
1402 }
1403 else
1404 {
1405 section = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
1406 }
1407
1408 if (section == null)
1409 {
1410 _errorStatus = ERROR_COULD_NOT_RETRIEVE_SECTION;
1411 return;
1412 }
1413
1414 Element existingContent = (Element) GSXML.getChildByTagName(section, GSXML.DOCXML_CONTENT_ELEM);
1415
1416 //If the current section does not have content then just add it, otherwise replace the existing one
1417 if (existingContent == null)
1418 {
1419 Element newContentElem = docXML.createElement(GSXML.DOCXML_CONTENT_ELEM);
1420 Node textNode = docXML.createTextNode(newContent);
1421 newContentElem.appendChild(textNode);
1422 }
1423 else
1424 {
1425 Node textNode = existingContent.getFirstChild();
1426 if (textNode != null)
1427 {
1428 textNode.setNodeValue(newContent);
1429 }
1430 else
1431 {
1432 existingContent.appendChild(docXML.createTextNode(newContent));
1433 }
1434 }
1435
1436 //Write the new change back into the file
1437 if (!writeXMLFile(docXML, oid, collection, userContext))
1438 {
1439 _errorStatus = ERROR_COULD_NOT_WRITE_TO_DOC_XML;
1440 return;
1441 }
1442 }
1443
1444 /**
1445 * Can be used to get the file path of the doc.xml file containing the given
1446 * OID.
1447 *
1448 * @param oid
1449 * is the identifier of the document/section to get the doc.xml
1450 * of.
1451 * @param collection
1452 * is the collection the document resides in.
1453 * @return the file path to the doc.xml file.
1454 */
1455 public String archiveGetDocumentFilePath(String oid, String collection, UserContext userContext)
1456 {
1457 _errorStatus = NO_ERROR;
1458
1459 if (oid.contains("."))
1460 {
1461 oid = oid.substring(0, oid.indexOf("."));
1462 }
1463
1464 String assocFilePath = getDocFilePathFromDatabase(oid, collection, userContext);
1465 if (assocFilePath == null)
1466 {
1467 _errorStatus = ERROR_DATA_NOT_FOUND_IN_DATABASE;
1468 return null;
1469 }
1470
1471 if (File.separator.equals("\\"))
1472 {
1473 assocFilePath = assocFilePath.replace("/", "\\");
1474 }
1475
1476 String docFilePath = _siteHome + File.separatorChar + "collect" + File.separatorChar + collection + File.separatorChar + "archives" + File.separatorChar + assocFilePath;
1477 return docFilePath;
1478 }
1479
1480 /**
1481 * Can be used to find the document that a specific source file is used in.
1482 *
1483 * @param srcFile
1484 * is the name of the source file.
1485 * @param collection
1486 * is the collection the source file resides in.
1487 * @return the OID of the document that the source file is used in.
1488 */
1489 public String archiveGetSourceFileOID(String srcFile, String collection, UserContext userContext)
1490 {
1491 _errorStatus = NO_ERROR;
1492 SimpleCollectionDatabase coll_db = openDatabase(collection, ARCHIVEINFSRC, SimpleCollectionDatabase.READ, userContext);
1493 if (coll_db == null)
1494 {
1495 _errorStatus = ERROR_COULD_NOT_OPEN_DATABASE;
1496 return null;
1497 }
1498
1499 DBInfo info = coll_db.getInfo(srcFile);
1500 if (info == null)
1501 {
1502 _errorStatus = ERROR_DATA_NOT_FOUND_IN_DATABASE;
1503 coll_db.closeDatabase();
1504 return null;
1505 }
1506
1507 String oid = info.getInfo("oid");
1508
1509 coll_db.closeDatabase();
1510 return oid;
1511 }
1512
1513 /**
1514 * Checks to see if a document or section at a given OID exists.
1515 *
1516 * @param oid
1517 * is the identifier of the document/section to check.
1518 * @param collection
1519 * is the collection to search in.
1520 * @return true if the document/section exists, false otherwise.
1521 */
1522 public boolean archiveCheckDocumentOrSectionExists(String oid, String collection, UserContext userContext)
1523 {
1524 _errorStatus = NO_ERROR;
1525 SimpleCollectionDatabase coll_db = openDatabase(collection, ARCHIVEINFDOC, SimpleCollectionDatabase.READ, userContext);
1526 if (coll_db == null)
1527 {
1528 _errorStatus = ERROR_COULD_NOT_OPEN_DATABASE;
1529 return false;
1530 }
1531
1532 boolean section = false;
1533 if (oid.contains("."))
1534 {
1535 section = true;
1536 }
1537
1538 DBInfo info = null;
1539 if (section)
1540 {
1541 info = coll_db.getInfo(oid.substring(0, oid.indexOf(".")));
1542 }
1543 else
1544 {
1545 info = coll_db.getInfo(oid);
1546 }
1547 boolean exists = (info != null);
1548
1549 coll_db.closeDatabase();
1550 if (section && exists)
1551 {
1552 Document docXML = getDocXML(oid, collection, userContext);
1553 if (getSectionBySectionNumber(docXML, getSectionFromOID(oid)) == null)
1554 {
1555 return false;
1556 }
1557 return true;
1558 }
1559
1560 return exists;
1561 }
1562
1563 /**
1564 * Can be used to write a series of entries to the document database.
1565 *
1566 * @param oid
1567 * is the key that the entries will be written to.
1568 * @param collection
1569 * is the collection whose database will be written to.
1570 * @param infoList
1571 * is the list of entries to write.
1572 */
1573 public void archiveWriteEntryToDatabase(String oid, String collection, HashMap<String, ArrayList<String>> infoList, UserContext userContext)
1574 {
1575 _errorStatus = NO_ERROR;
1576 if (oid == null || oid.equals(""))
1577 {
1578 _errorStatus = ERROR_OID_NOT_SPECIFIED;
1579 return;
1580 }
1581 else if (collection == null || collection.equals(""))
1582 {
1583 _errorStatus = ERROR_COLLECTION_NOT_SPECIFIED;
1584 return;
1585 }
1586
1587 DBInfo info = new DBInfo();
1588
1589 for (String s : infoList.keySet())
1590 {
1591 for (String v : infoList.get(s))
1592 {
1593 info.addInfo(s, v);
1594 }
1595 }
1596
1597 SimpleCollectionDatabase coll_db = openDatabase(collection, ARCHIVEINFDOC, SimpleCollectionDatabase.WRITE, userContext);
1598 if (coll_db == null)
1599 {
1600 _errorStatus = ERROR_COULD_NOT_OPEN_DATABASE;
1601 return;
1602 }
1603 coll_db.setInfo(oid, info);
1604 coll_db.closeDatabase();
1605 }
1606
1607 /**
1608 * Can be used to remove an entry from the document database.
1609 *
1610 * @param oid
1611 * is the key of the entry to erase.
1612 * @param collection
1613 * is the collection whose database will have the entry removed.
1614 */
1615 public void archiveRemoveEntryFromDatabase(String oid, String collection, UserContext userContext)
1616 {
1617 _errorStatus = NO_ERROR;
1618 if (oid == null || oid.equals(""))
1619 {
1620 _errorStatus = ERROR_OID_NOT_SPECIFIED;
1621 return;
1622 }
1623 else if (collection == null || collection.equals(""))
1624 {
1625 _errorStatus = ERROR_COLLECTION_NOT_SPECIFIED;
1626 return;
1627 }
1628
1629 SimpleCollectionDatabase coll_db = openDatabase(collection, ARCHIVEINFDOC, SimpleCollectionDatabase.WRITE, userContext);
1630 if (coll_db == null)
1631 {
1632 _errorStatus = ERROR_COULD_NOT_OPEN_DATABASE;
1633 return;
1634 }
1635 coll_db.deleteKey(oid);
1636 coll_db.closeDatabase();
1637 }
1638
1639 /**
1640 * Gets the list of associated files for a given document.
1641 *
1642 * @param oid
1643 * is the identifier that will be used to search for associated
1644 * documents.
1645 * @param collection
1646 * is the collection whose database will be searched.
1647 * @return the list of associated files.
1648 */
1649 public ArrayList<String> archiveGetAssociatedImportFiles(String oid, String collection, UserContext userContext)
1650 {
1651 _errorStatus = NO_ERROR;
1652 if (oid == null || oid.equals(""))
1653 {
1654 _errorStatus = ERROR_OID_NOT_SPECIFIED;
1655 return null;
1656 }
1657 else if (collection == null || collection.equals(""))
1658 {
1659 _errorStatus = ERROR_COLLECTION_NOT_SPECIFIED;
1660 return null;
1661 }
1662
1663 SimpleCollectionDatabase coll_db = openDatabase(collection, ARCHIVEINFDOC, SimpleCollectionDatabase.READ, userContext);
1664 if (coll_db == null)
1665 {
1666 _errorStatus = ERROR_COULD_NOT_OPEN_DATABASE;
1667 return null;
1668 }
1669
1670 DBInfo info = coll_db.getInfo(oid);
1671 if (info == null)
1672 {
1673 _errorStatus = ERROR_DATA_NOT_FOUND_IN_DATABASE;
1674 coll_db.closeDatabase();
1675 return null;
1676 }
1677
1678 String srcFile = info.getInfo("src-file");
1679 Vector data = info.getMultiInfo("assoc-file");
1680
1681 ArrayList<String> assocFiles = new ArrayList<String>();
1682 assocFiles.add(srcFile);
1683 for (String d : (Vector<String>) data)
1684 {
1685 assocFiles.add(d);
1686 }
1687
1688 coll_db.closeDatabase();
1689 return assocFiles;
1690 }
1691
1692 /********************
1693 * Helper functions *
1694 *******************/
1695
1696 public boolean checkError(Element elem, String methodName)
1697 {
1698 if (_errorMessageMap.get(_errorStatus) != null)
1699 {
1700 GSXML.addError(elem.getOwnerDocument(), elem, methodName + ": " + _errorMessageMap.get(_errorStatus), GSXML.ERROR_TYPE_SYNTAX);
1701 return true;
1702 }
1703
1704 return false;
1705 }
1706
1707 public String getSectionFromOID(String oid)
1708 {
1709 if (!oid.contains("."))
1710 {
1711 return null;
1712 }
1713 return oid.substring(oid.indexOf(".") + 1);
1714 }
1715
1716 public Element createElementWithValue(Document doc, String nodeName, String name, String value)
1717 {
1718 Element metadataElem = doc.createElement(nodeName);
1719 metadataElem.setAttribute(GSXML.NAME_ATT, name);
1720 Node textNode = doc.createTextNode(value);
1721 metadataElem.appendChild(textNode);
1722 return metadataElem;
1723 }
1724
1725 public int getOperation(String to, String from)
1726 {
1727 int op;
1728 if (!to.contains(".") && !from.contains("."))
1729 {
1730 op = OPERATION_TYPE_DOC_TO_DOC;
1731 }
1732 else if (!to.contains(".") && from.contains("."))
1733 {
1734 op = OPERATION_TYPE_DOC_TO_SEC;
1735 }
1736 else if (to.contains(".") && !from.contains("."))
1737 {
1738 op = OPERATION_TYPE_SEC_TO_DOC;
1739 }
1740 else
1741 {
1742 op = OPERATION_TYPE_SEC_TO_SEC;
1743 }
1744 return op;
1745 }
1746
1747 public int[] oidToSectionNumberArray(String oid) throws Exception
1748 {
1749 String[] strLevels = oid.split("\\.");
1750 int[] intLevels = new int[strLevels.length - 1];
1751
1752 for (int i = 1; i < strLevels.length; i++) //Start at 1 to avoid the document identifier part of the OID
1753 {
1754 intLevels[i - 1] = Integer.parseInt(strLevels[i]);
1755 }
1756
1757 return intLevels;
1758 }
1759
1760 public String getDocFilePathFromDatabase(String oid, String collection, UserContext userContext)
1761 {
1762 SimpleCollectionDatabase coll_db = openDatabase(collection, ARCHIVEINFDOC, SimpleCollectionDatabase.WRITE, userContext);
1763 if (coll_db == null)
1764 {
1765 return null;
1766 }
1767 DBInfo info = coll_db.getInfo(oid);
1768 if (info == null)
1769 {
1770 return null;
1771 }
1772
1773 String docFile = info.getInfo("doc-file");
1774 coll_db.closeDatabase();
1775 return docFile;
1776 }
1777
1778 public boolean deleteDirectory(File current)
1779 {
1780 try
1781 {
1782 if (current == null || !current.exists())
1783 {
1784 return false;
1785 }
1786
1787 if (!current.isDirectory())
1788 {
1789 current.delete();
1790 return true;
1791 }
1792
1793 for (File f : current.listFiles())
1794 {
1795 if (f.isDirectory())
1796 {
1797 deleteDirectory(f);
1798 }
1799 else
1800 {
1801 f.delete();
1802 }
1803 }
1804 current.delete();
1805 }
1806 catch (Exception ex)
1807 {
1808 return false;
1809 }
1810
1811 return true;
1812 }
1813
1814 public int checkOIDandCollection(String oid, String collection, UserContext userContext)
1815 {
1816 if (oid == null || oid.equals(""))
1817 {
1818 return ERROR_OID_NOT_SPECIFIED;
1819 }
1820
1821 if (collection == null || collection.equals(""))
1822 {
1823 return ERROR_COLLECTION_NOT_SPECIFIED;
1824 }
1825
1826 if (!archiveCheckDocumentOrSectionExists(oid, collection, userContext))
1827 {
1828 return ERROR_SOURCE_DOCUMENT_OR_SECTION_DOES_NOT_EXIST;
1829 }
1830 return NO_ERROR;
1831 }
1832
1833 protected static String generateOID()
1834 {
1835 return "temp";
1836 }
1837
1838 public boolean copyDirectory(File src, File dest)
1839 {
1840 if (src.isDirectory())
1841 {
1842 //If the destination directory does not exist then create it
1843 if (!dest.exists())
1844 {
1845 dest.mkdir();
1846 }
1847
1848 //Get all the files in the directory
1849 String files[] = src.list();
1850 for (String file : files)
1851 {
1852 File srcFile = new File(src, file);
1853 File destFile = new File(dest, file);
1854
1855 if (!copyDirectory(srcFile, destFile))
1856 {
1857 return false;
1858 }
1859 }
1860 }
1861 else
1862 {
1863 try
1864 {
1865 FileChannel in = new FileInputStream(src).getChannel();
1866 FileChannel out = new FileOutputStream(dest).getChannel();
1867
1868 in.transferTo(0, in.size(), out);
1869
1870 in.close();
1871 out.close();
1872 }
1873 catch (Exception ex)
1874 {
1875 ex.printStackTrace();
1876 return false;
1877 }
1878 }
1879 return true;
1880 }
1881
1882 public Element getTopLevelSectionElement(Document docXML)
1883 {
1884 return (Element) GSXML.getChildByTagName(docXML.getDocumentElement(), GSXML.DOCXML_SECTION_ELEM);
1885 }
1886
1887 public boolean writeXMLFile(Document doc, String oid, String collection, UserContext userContext)
1888 {
1889 try
1890 {
1891 DOMSource source = new DOMSource(doc);
1892
1893 String test = archiveGetDocumentFilePath(oid, collection, userContext);
1894 File xmlFile = new File(test);
1895 Result result = new StreamResult(xmlFile);
1896
1897 Transformer transformer = TransformerFactory.newInstance().newTransformer();
1898 transformer.transform(source, result);
1899 }
1900 catch (Exception ex)
1901 {
1902 return false;
1903 }
1904 return true;
1905 }
1906
1907 public Document getDocXML(String oid, String collection, UserContext userContext)
1908 {
1909 if (oid.contains("."))
1910 {
1911 oid = oid.substring(0, oid.indexOf("."));
1912 }
1913
1914 Document docXML = null;
1915 if ((docXML = _docCache.get(oid + "__" + collection)) == null)
1916 {
1917 String filePath = archiveGetDocumentFilePath(oid, collection, userContext);
1918 File docFile = new File(filePath);
1919
1920 if (!docFile.exists())
1921 {
1922 return null;
1923 }
1924
1925 try
1926 {
1927 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
1928 DocumentBuilder db = dbf.newDocumentBuilder();
1929 docXML = db.parse(docFile);
1930
1931 _docCache.put(oid + "__" + collection, docXML);
1932 }
1933 catch (Exception ex)
1934 {
1935 return null;
1936 }
1937 }
1938 return docXML;
1939 }
1940
1941 public ArrayList<Element> getMetadataElementsFromSection(Document docXML, String oid, String metadataName)
1942 {
1943 if (oid.contains("."))
1944 {
1945 Element section = getSectionBySectionNumber(docXML, getSectionFromOID(oid));
1946 return getMetadataElementsFromSection(section, metadataName);
1947 }
1948 else
1949 {
1950 return getMetadataElementsFromSection(getTopLevelSectionElement(docXML), metadataName);
1951 }
1952 }
1953
1954 public ArrayList<Element> getMetadataElementsFromSection(Element section, String metadataName)
1955 {
1956 Element description = (Element) GSXML.getChildByTagName(section, GSXML.DOCXML_DESCRIPTION_ELEM);
1957 if (description == null)
1958 {
1959 return null;
1960 }
1961
1962 ArrayList<Element> elemList = new ArrayList<Element>();
1963 NodeList metadataNodes = GSXML.getChildrenByTagName(description, GSXML.DOCXML_METADATA_ELEM);
1964 for (int j = 0; j < metadataNodes.getLength(); j++)
1965 {
1966 //If this is a metadata element with the requested name then we have found what we are looking for
1967 if (((Element) metadataNodes.item(j)).getAttribute(GSXML.NAME_ATT).equals(metadataName))
1968 {
1969 elemList.add((Element) metadataNodes.item(j));
1970 }
1971 }
1972
1973 return elemList;
1974 }
1975
1976 public Element getSectionBySectionNumber(Document docXML, String sectionNum)
1977 {
1978 return getSectionBySectionNumber(getTopLevelSectionElement(docXML), sectionNum);
1979 }
1980
1981 public Element getSectionBySectionNumber(Element current, String sectionNum)
1982 {
1983 if (sectionNum == null || sectionNum.equals(""))
1984 {
1985 return current;
1986 }
1987
1988 try
1989 {
1990 String[] levels = sectionNum.split("\\.");
1991 int currentSectionNum = Integer.parseInt(levels[0]);
1992
1993 NodeList sections = GSXML.getChildrenByTagName(current, GSXML.DOCXML_SECTION_ELEM);
1994 if (levels.length > 1)
1995 {
1996 return getSectionBySectionNumber((Element) sections.item(currentSectionNum - 1), sectionNum.substring(sectionNum.indexOf(".") + 1));
1997 }
1998 else
1999 {
2000 return (Element) sections.item(currentSectionNum - 1);
2001 }
2002 }
2003 catch (Exception ex)
2004 {
2005 return null;
2006 }
2007 }
2008
2009 public String getDatabaseTypeFromCollection(String collection, UserContext userContext)
2010 {
2011 //Find out what kind of database we have
2012 Element dbTypeMessage = _mainDoc.createElement(GSXML.MESSAGE_ELEM);
2013 Element dbTypeRequest = GSXML.createBasicRequest(_mainDoc, GSXML.REQUEST_TYPE_DESCRIBE, collection, userContext);
2014 dbTypeMessage.appendChild(dbTypeRequest);
2015 Element dbTypeResponse = (Element) _router.process(dbTypeMessage);
2016
2017 String path = GSPath.appendLink(GSXML.RESPONSE_ELEM, GSXML.COLLECTION_ELEM);
2018 Element collectionElem = (Element) GSXML.getNodeByPath(dbTypeResponse, path);
2019
2020 if (collectionElem != null)
2021 {
2022 return collectionElem.getAttribute(GSXML.DB_TYPE_ATT);
2023 }
2024 return "gdbm"; //The default collection database type
2025 }
2026
2027 public SimpleCollectionDatabase openDatabase(String collection, String dbName, int readWrite, UserContext userContext)
2028 {
2029 //Find out what kind of database we have
2030 String databaseType = getDatabaseTypeFromCollection(collection, userContext);
2031 String dbExt = DBHelper.getDBExtFromDBType(databaseType);
2032
2033 SimpleCollectionDatabase coll_db = new SimpleCollectionDatabase(databaseType);
2034 if (!coll_db.databaseOK())
2035 {
2036 System.err.println("Couldn't create the collection database of type " + databaseType);
2037 return null;
2038 }
2039
2040 coll_db.openDatabase(GSFile.collectionArchiveDir(_siteHome, collection) + File.separatorChar + dbName + dbExt, readWrite);
2041
2042 return coll_db;
2043 }
2044
2045 public int operationStringToInt(String operation)
2046 {
2047 if (operation.equals("insertBefore"))
2048 {
2049 return OPERATION_INSERT_BEFORE;
2050 }
2051 else if (operation.equals("insertAfter"))
2052 {
2053 return OPERATION_INSERT_AFTER;
2054 }
2055 else if (operation.equals("append"))
2056 {
2057 return OPERATION_APPEND;
2058 }
2059 else
2060 {
2061 return OPERATION_REPLACE;
2062 }
2063 }
2064
2065 public int getErrorStatus()
2066 {
2067 return _errorStatus;
2068 }
2069}
Note: See TracBrowser for help on using the repository browser.