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

Last change on this file since 28965 was 28965, checked in by kjdon, 10 years ago

removing stored Document field - now get passed it as needed or work it out from Element owner documents

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