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

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

Some updates and formatting changes

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