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

Last change on this file since 36107 was 36107, checked in by kjdon, 2 years ago

greenstone.org now https

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