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

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

Fixing Greenstone 3's use (or lack thereof) of generics, this was done automatically so we may want to change it over time. This change will also auto-format any files that have not already been formatted.

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