DBA Data[Home] [Help]

PACKAGE BODY: APPS.JDR_UTILS

Source


1 PACKAGE BODY JDR_UTILS AS
2 /* $Header: JDRUTEXB.pls 120.3 2005/10/26 06:15:47 akbansal noship $ */
3 
4   NEWLINE CONSTANT VARCHAR2(1) := '
5 ';
6 
7   -----------------------------------------------------------------------------
8   ---------------------------- PRIVATE METHODS --------------------------------
9   -----------------------------------------------------------------------------
10 
11   -- Gets the document ID for a fully qualified document name.
12   --
13   -- Parameters:
14   --  p_document        - fully qualified name
15   --
16   --  p_type            - the type of document to search for.  In the jrad
17   --                      repository, there are the following types of
18   --                      jrad paths.
19   --                      (1) Document file - XML file representing a document
20   --                      (2) Package file - XML file representing a package
21   --                      (3) Child document - document inside a package file
22   --                      (4) Package directory - a directory
23   --
24   --                      This parameter can be one of the following:
25   --                      DOCUMENT - matches (1) and (3) above
26   --                      PACKAGE  - matches (2) and (4) above
27   --                      FILE     - matches (1) and (2) above
28   --                      NONPATH  - matches (1) and (2) and (3)
29   --                      PATH     - matches (4) above
30   --                      ANY      - matches (1) and (2) and (3) and (4) above
31   FUNCTION getDocumentID(
32     p_document VARCHAR2,
33     p_type     VARCHAR2 DEFAULT 'ANY') RETURN jdr_paths.path_docid%TYPE
34   IS
35     docID    jdr_paths.path_docid%TYPE;
36     pathType jdr_paths.path_type%TYPE;
37     pathSeq  jdr_paths.path_seq%TYPE;
38   BEGIN
39     -- Get the ID of the document
40     docID := jdr_mds_internal.getDocumentID(p_document);
41 
42     -- Verify that we have found a document of the correct type
43     IF ((docID <> -1) AND (p_type <> 'ANY')) THEN
44       SELECT path_type, path_seq INTO pathType, pathSeq
45       FROM jdr_paths WHERE path_docid = docID;
46 
47       IF (p_type = 'FILE') THEN
48         -- Make sure we are dealing with a document or package file
49         IF ((pathType = 'DOCUMENT' AND pathSeq = -1) OR
50             (pathType = 'PACKAGE' AND pathSeq = 0)) THEN
51           RETURN (docID);
52         END IF;
53       ELSIF (p_type = 'DOCUMENT') THEN
54         -- Make sure we are dealing with a document
55         IF (pathType = 'DOCUMENT') THEN
56           RETURN (docID);
57         END IF;
58       ELSIF (p_type = 'PACKAGE') THEN
59         IF (pathType = 'PACKAGE') THEN
60           RETURN (docID);
61         END IF;
62       ELSIF (p_type = 'PATH') THEN
63         IF ((pathType = 'PACKAGE') AND (pathSeq = -1)) THEN
64           RETURN (docID);
65         END IF;
66       ELSIF (p_type = 'NONPATH') THEN
67         IF ((pathType <> 'PACKAGE') OR (pathSeq = 0)) THEN
68           RETURN (docID);
69         END IF;
70       END IF;
71 
72       -- No match found
73       RETURN (-1);
74     END IF;
75 
76     RETURN (docID);
77 
78   EXCEPTION
79     WHEN OTHERS THEN
80       RETURN (-1);
81   END;
82 
83 
84   -- Prints a chunk of JRAD XML.  Since we are using DBMS_OUPUT to print
85   -- the document to the console and since DBMS_OUPUT has lots of limitations,
86   -- this procedure is a lot more complicated that it should be.
87   --
88   -- Parameters:
89   --  p_chunk           - the chunk of XML to print
90   --
91   --  p_maxLineSize     - the maximum allowable size for the line.
92   --
93   --  p_unclosedQuote  - TRUE indicates that the previous line was in the
94   --                     middle of a name/value pair.  Also, if the current
95   --                     line is in the middle of a name/value pair, this
96   --                     will be set to TRUE; if not, this will be set to
97   --                     FALSE.
98   --
99   --  p_indent         - amount of whitespace which the current line should be
100   --                     indented.  ALso, this will be set to the amount of
101   --                     whitespace the next line should be indented.  This
102   --                     is needed for when an element does not completely
103   --                     fit on one line.
104   --
105   PROCEDURE printChunk(
106     p_chunk         IN     VARCHAR2,
107     p_maxLineSize   IN     NUMBER,
108     p_unclosedQuote IN OUT NOCOPY /* file.sql.39 change */ BOOLEAN,
109     p_indent        IN OUT NOCOPY /* file.sql.39 change */ NUMBER)
110   IS
111     left                   VARCHAR2(255);
112     right                  VARCHAR2(32767);
113     tmppos                 NUMBER;
114     pos                    NUMBER;
115     len                    NUMBER;
116   BEGIN
117     -- If the chunk is less than the maximum line size (including the
118     -- starting position), then all we need to do is simply print the chunk
119     -- and reset the starting position.
120     len := LENGTH(p_chunk) + p_indent;
121     IF (len <= p_maxLineSize) THEN
122       DBMS_OUTPUT.PUT_LINE(LPAD(p_chunk, len));
123 
124       -- As we are "ending" an element, we can safely reset the "state"
125       p_indent := 0;
126       p_unclosedQuote := FALSE;
127 
128       RETURN;
129     END IF;
130 
131     -- We could not put the chunk in one line, so we have some work to do.
132     -- Here is an explanation of the general algorithm...
133     --
134     -- (1) Find the position in which the element ends.  If it ends before
135     --     the maximum line size is ended, then we can print the element here
136     --     and recursively call printChunk() to print the remaining portion
137     --     of the chunk.
138     --
139     -- (2) If the element can not fit on the line, then we will print as
140     --     many name/value pairs of the element which will fit on the line.
141     --     And to make the printing "prettier", we will indent the next line
142     --     to the beginning of where the first name/value pair started.
143     --
144     -- (3) If not a single name/value pair can fit on the line, then we attempt
145     --     to break up the name/value pair using whitespace; and set the
146     --     p_unclosedQuote parameter to indicate we are in the middle of a
147     --     name/value pair.
148     --
149     -- (4) If we can not even break up the name/value pair using whitespace,
150     --     then we simply print as much of the value as we can and hope that
151     --     it will look ok.
152     pos := INSTR(p_chunk, NEWLINE, 1);
153     IF (pos = 0) THEN
154       pos := INSTR(p_chunk, '>', 1);
155     ELSE
156       pos := pos - 1;
157     END IF;
158 
159     -- There should always be an end tag.  If not something is very wrong.
160     IF (pos = 0) THEN
161       DBMS_OUTPUT.PUT_LINE('Error printing document, no end tag encountered.');
162       RETURN;
163     END IF;
164 
165     -- If the current element can not fit on the line, so we will have to put
166     -- part of the element on this line, and the rest on future lines.
167     IF ((pos + p_indent) > p_maxLineSize) THEN
168       tmppos := 0;
169       pos := 0;
170       left := SUBSTR(p_chunk, 1, p_maxLineSize - p_indent);
171       LOOP
172         -- Find as many name/value pairs as will fit on this line.  If
173         -- p_unclosedQuote is TRUE, then the previous line did not complete
174         -- the name/value pair, so we just need to look for one double quote.
175         -- Otherwise, two double quotes indicate that a name/value pair is
176         -- ended.
177         IF (p_unclosedQuote) THEN
178           tmppos := INSTR(left, '"', pos + 1, 1);
179         ELSE
180           tmppos := INSTR(left, '"', pos + 1, 2);
181         END IF;
182 
183         -- Look for another name/value pair if we found a match.
184         IF (tmppos = 0) THEN
185           IF (pos > 0) THEN
186             -- If we found at least one name/value pair, then we are ok to
187             -- print the parial element.  This corresponds to (2) above.
188             p_unclosedQuote := FALSE;
189             EXIT;
190           ELSE
191             -- We are not able to put a name/value pair on the current line.
192             -- This is likely because of a very long value.  The best thing we
193             -- can do is try to end the line on a space.  If we do find a
194             -- space, this will correspond to (3) above.
195             pos := INSTR(left, ' ', -1);
196             IF (pos = 0) THEN
197               -- We have a very long value with no spaces in it - this
198               -- corresponds to (4) above.
199               pos := p_maxLineSize - p_indent;
200             END IF;
201             p_unclosedQuote := TRUE;
202             EXIT;
203           END IF;
204         END IF;
205 
206      	  -- A name/value pair which fits on the current line has been found.
207         -- Save the position and check if any more name/value pairs can fit on
208         -- this line.
209         pos := tmppos;
210       END LOOP;
211 
212       -- Print the partial element
213       left := LPAD(SUBSTR(p_chunk, 1, pos), pos + p_indent);
214       DBMS_OUTPUT.PUT_LINE(left);
215 
216       -- Remember indentation for the next line
217       IF (p_indent = 0) THEN
218         p_indent := LENGTH(left) -
219                     LENGTH(LTRIM(left)) +
220                     INSTR(LTRIM(left), ' ', 1);
221       END IF;
222 
223       -- Get the remaining portion of the chunk
224       right := LTRIM(SUBSTR(p_chunk, pos + 1));
225     ELSE
226       -- This corresponds to (1) above, the element fitting on one line.
227       -- Print the element
228       left := LPAD(SUBSTR(p_chunk, 1, pos), pos + p_indent);
229       DBMS_OUTPUT.PUT_LINE(left);
230 
231       -- As we are "ending" an element, we can safely reset the "state"
232       p_indent := 0;
233       p_unclosedQuote := FALSE;
234 
235       -- Get the remaining portion of the chunk
236       tmppos := INSTR(p_chunk, NEWLINE, pos + 1);
237       right := SUBSTR(p_chunk, tmppos + 1);
238     END IF;
239 
240     -- Print the remaining string
241     printChunk(right, p_maxLineSize, p_unclosedQuote, p_indent);
242   END;
243 
244 
245   PROCEDURE printChunk(p_chunk IN VARCHAR2, p_maxLineSize NUMBER)
246   IS
247     unclosedQuote  BOOLEAN := FALSE;
248     indent         NUMBER  := 0;
249   BEGIN
250     printChunk(p_chunk, p_maxLineSize, unclosedQuote, indent);
251   END;
252 
253 
254   -----------------------------------------------------------------------------
255   ---------------------------- PUBLIC METHODS ---------------------------------
256   -----------------------------------------------------------------------------
257 
258   -- Deletes the document from the repository.
259   --
260   -- Parameters:
261   --  p_document    - the fully qualified document name, which can represent
262   --                  either a document or package file.
263   --                  (i.e.  '/oracle/apps/ak/attributeSets')
264   --
265   PROCEDURE deleteDocument(p_document VARCHAR2)
266   IS
267     docID    JDR_PATHS.PATH_DOCID%TYPE;
268   BEGIN
269     -- Get the ID of the document
270     docID := getDocumentID(p_document, 'FILE');
271     IF (docID = -1) THEN
272       dbms_output.put_line('Error: Could not find document ' || p_document);
273       RETURN;
274     END IF;
275 
276     -- Drop the document
277     jdr_mds_internal.dropDocument(docID);
278     dbms_output.put_line('Successfully deleted document ' || p_document || '.');
279   END;
280 
281   -- Deletes all empty customization documents from the repository
282   PROCEDURE deleteEmptyCustomizations
283   IS
284     CURSOR c_docs IS
285       SELECT path_docid
286       FROM jdr_paths
287       WHERE jdr_mds_internal.getDocumentName(path_docid)
288             LIKE '%/customizations/%' AND
289             path_type = 'DOCUMENT';
290     CURSOR c_comps(docID JDR_COMPONENTS.COMP_DOCID%TYPE) IS
291       SELECT comp_element
292       FROM jdr_components
293       WHERE comp_docid = docID;
294     CURSOR c_atts(docID  JDR_ATTRIBUTES.ATT_COMP_DOCID%TYPE,
295                   compID JDR_ATTRIBUTES.ATT_COMP_SEQ%TYPE) IS
296       SELECT att_name
297       FROM jdr_attributes
298       WHERE att_comp_docid = docID AND
299             att_comp_seq = compID;
300     docID     JDR_PATHS.PATH_DOCID%TYPE;
301     compElem  JDR_COMPONENTS.COMP_ELEMENT%TYPE;
302     attName   JDR_ATTRIBUTES.ATT_NAME%TYPE;
303     compsIsEmpty BOOLEAN;
304     attsIsEmpty  BOOLEAN;
305   BEGIN
306     dbms_output.enable(1000000);
307     -- Loop through all customization documents in the repository
308     OPEN c_docs;
309     LOOP
310       FETCH c_docs INTO docID;
311       IF (c_docs%NOTFOUND) THEN
312         CLOSE c_docs;
313         EXIT;
314       END IF;
315       compsIsEmpty := TRUE;
316       attsIsEmpty  := FALSE;
317       -- For each document, loop through all component names
318       OPEN c_comps(docID);
319       LOOP
320         FETCH c_comps INTO compElem;
321         IF (c_comps%NOTFOUND) THEN
322           CLOSE c_comps;
323           EXIT;
324         END IF;
325         -- If the component name is not 'customization', 'modifications', or
326         -- 'view', then this is not an empty customization document.
327         -- Modifications may be an element, rather than a grouping, if it
328         -- appears in the form <modifications/>
329         IF compElem <> 'customization' AND
330            compElem <> 'modifications'  AND
331            compElem <> 'view' THEN
332           compsIsEmpty := FALSE;
333           CLOSE c_comps;
334           EXIT;
335         END IF;
336       END LOOP;
337       IF compsIsEmpty THEN
338         attsIsEmpty := TRUE;
339         -- Look at all attributes of the <customization> element.
340         OPEN c_atts(docID, 0);
341         LOOP
342           FETCH c_atts INTO attName;
343           IF (c_atts%NOTFOUND) THEN
344             CLOSE c_atts;
345             EXIT;
346           END IF;
347           IF attName <> 'customizes'    AND
348              attName <> 'xml:lang'      AND
349              attName <> 'version'       AND
350              attName <> 'file-version'  AND
351              attName <> 'developerMode' AND
352              attName <> 'MDSActiveDoc'  AND
353              instr(attName, 'xmlns') <> 1  THEN
354             attsIsEmpty := FALSE;
355             CLOSE c_atts;
356             EXIT;
357           END IF;
358         END LOOP;
359       END IF;
360       IF attsIsEmpty THEN
361         -- This is an empty customization document
362         dbms_output.put_line('Deleting ' ||
363                              jdr_mds_internal.getDocumentName(docID));
364         jdr_mds_internal.dropDocument(docID);
365       END IF;
366     END LOOP;
367   END;
368 
369 
370   -- Deletes the package from the repository if the package is empty.
371   --
372   -- Parameters:
373   --  p_package    - the fully qualified package name
374   --                 (i.e.  '/oracle/apps')
375   --
376   PROCEDURE deletePackage(p_package VARCHAR2)
377   IS
378     docID    JDR_PATHS.PATH_DOCID%TYPE;
379     contents INTEGER;
380   BEGIN
381     -- Get the ID of the document
382     docID := getDocumentID(p_package, 'PATH');
383     IF (docID = -1) THEN
384       dbms_output.put_line('Error: Could not find package ' || p_package);
385       RETURN;
386     END IF;
387 
388     -- Make sure that the package is empty
389     SELECT COUNT(*) INTO contents
390     FROM jdr_paths
391     WHERE path_owner_docid = docID;
392 
393     IF (contents <> 0) THEN
394       dbms_output.put_line('Error: Unable to delete ' || p_package ||
395                            ' since it contains documents and/or packages.');
396       RETURN;
397     END IF;
398 
399     jdr_mds_internal.dropDocument(docID);
400     dbms_output.put_line('Successfully deleted package ' || p_package || '.');
401   END;
402 
403 
404   --
405   -- Export the XML for a document and pass it back in 32k chunks.  This
406   -- function will return XML chunks, with a maximum size of 32k.
407   FUNCTION exportDocument(
408     p_document           VARCHAR2,
409     p_exportFinished OUT NOCOPY /* file.sql.39 change */ BOOLEAN,
410     p_formatted          BOOLEAN DEFAULT TRUE) RETURN VARCHAR2
411   IS
412     chunk          VARCHAR2(32000);
413     exportFinished INTEGER;
414     formatted      INTEGER;
415   BEGIN
416     IF (p_formatted) THEN
417       formatted := 1;
418     ELSE
419       formatted := 0;
420     END IF;
421 
422     -- Get the the contents of the XML document.  If p_document is not null,
423     -- this will retrieve the first chunk of the document.  If p_document is
424     -- null, then we are retrieving a subsequent chunk of the document.
425     --
426     -- It's a little ugly that we have to switch between INTEGERs and BOOLEANs,
427     -- but jdr_mds_internal.exportDocumentAsXML is called from JDBC and,
428     -- unfortunately, JDBC does not have support for BOOLEAN parameters, so
429     -- that's why we have to do the conversion.
430     chunk := jdr_mds_internal.exportDocumentAsXML(exportFinished,
431                                                   p_document,
432                                                   formatted);
433 
434     p_exportFinished := (exportFinished = 1);
435 
436     return (chunk);
437   END;
438 
439 
440   -- Gets the fully qualified name of the component.
441   --
442   -- Parameters:
443   --  p_docid       - the ID of the document which contains the component
444   --
445   --  p_compid      - the ID of the component (from comp_id in the
446   --                  jdr_components table
447   --
448   FUNCTION getComponentName(
449     p_docid  jdr_paths.path_docid%TYPE,
450     p_compid jdr_components.comp_id%TYPE) RETURN VARCHAR2
451   IS
452     separator  VARCHAR2(1) := NULL;
453   BEGIN
454     IF (p_compid IS NOT NULL) THEN
455       separator := '.';
456     END IF;
457 
458     RETURN (getDocumentName(p_docid)||separator||p_compid);
459   END;
460 
461 
462   -- Gets the fully qualified name of the document.
463   --
464   -- Parameters:
465   --  p_docid       - the ID of the document
466   FUNCTION getDocumentName(
467     p_docid  jdr_paths.path_docid%TYPE) RETURN VARCHAR2
468   IS
469   BEGIN
470     RETURN (jdr_mds_internal.getDocumentName(p_docid));
471   END;
472 
473 
474   -- Gets all of the translations of the specified document.
475   FUNCTION getTranslations(
476     p_document VARCHAR2) RETURN translationList
477   IS
478     CURSOR cTrans(p_docID jdr_paths.path_docid%TYPE) IS
479       SELECT
480         atl_lang, atl_comp_ref, atl_name, atl_value
481       FROM
482         jdr_attributes_trans
483       WHERE
484         atl_comp_docid = p_docID
485       ORDER BY
486         atl_lang;
487 
488     docID      jdr_paths.path_docid%TYPE;
489     trans      translationList;
490     pos        BINARY_INTEGER;
491   BEGIN
492     -- Get the document ID for this document
493     docID := getDocumentID(p_document, 'FILE');
494     IF (docID = -1) THEN
495       RAISE no_such_document;
496     END IF;
497 
498     -- Fetch all of the translations into the translation list
499     pos := 0;
500     FOR tranRec IN cTrans(docID) LOOP
501       IF (pos = 0) THEN
502         -- Initialize the translation list
503         trans := translationList(NULL);
504       ELSE
505         -- Extend the list to make room for this translation
506         trans.EXTEND;
507       END IF;
508 
509       pos := pos + 1;
510       trans(pos) := tranRec;
511 
512       -- Since the compref attribute can not be NULL (as it is part of an index),
513       -- we have to save NULL values as '.' in the database.  As this might
514       -- be confusing to users, we revert the '.' back to NULL.
515       IF (tranRec.atl_comp_ref = '.') THEN
516         trans(pos).compref := '';
517       END IF;
518     END LOOP;
519 
520     RETURN (trans);
521   EXCEPTION
522     WHEN NO_DATA_FOUND THEN
523       RETURN (NULL);
524   END;
525 
526 
527   -- Lists the contents of a package.
528   PROCEDURE listContents(p_path VARCHAR2, p_recursive BOOLEAN DEFAULT FALSE)
529   IS
530     -- Selects documents in the current directory
531     CURSOR c_docs(docid JDR_PATHS.PATH_DOCID%TYPE) IS
532       SELECT
533         jdr_mds_internal.getDocumentName(path_docid), path_type, path_seq
534       FROM
535         jdr_paths
536       WHERE
537         path_owner_docid = docid AND
538         ((path_type = 'DOCUMENT' AND path_seq = -1) OR
539          (path_type = 'PACKAGE' AND (path_seq = 0 OR path_seq = -1)));
540 
541     -- Selects documents in the current directory, plus its children
542     CURSOR c_alldocs(docid JDR_PATHS.PATH_DOCID%TYPE) IS
543       SELECT
544         jdr_mds_internal.getDocumentName(path_docid), path_type, path_seq
545       FROM
546         (SELECT path_docid, path_type, path_seq
547          FROM jdr_paths
548          START WITH path_owner_docid = docid
549          CONNECT BY PRIOR path_docid = path_owner_docid) paths
550       WHERE
551         (path_type = 'DOCUMENT' AND path_seq = -1) OR
552         (path_type = 'PACKAGE' AND path_seq = 0) OR
553         (path_type = 'PACKAGE' AND path_seq = -1 AND
554          NOT EXISTS (SELECT * FROM jdr_paths
555                      WHERE path_owner_docid = paths.path_docid));
556 
557     docID    JDR_PATHS.PATH_DOCID%TYPE;
558     pathSeq  JDR_PATHS.PATH_SEQ%TYPE;
559     pathType JDR_PATHS.PATH_TYPE%TYPE;
560     docname  VARCHAR2(1024);
561   BEGIN
562     dbms_output.enable(1000000);
563 
564     docID := getDocumentID(p_path, 'ANY');
565 
566     -- Nothing to do if the path does not exist
567     IF (docID = -1) THEN
568       dbms_output.put_line('Error: Could not find path ' || p_path);
569       RETURN;
570     END IF;
571 
572     IF (p_recursive) THEN
573       dbms_output.put_line('Printing contents of ' || p_path || ' recursively');
574       OPEN c_alldocs(docID);
575       LOOP
576         FETCH c_alldocs INTO docname, pathType, pathSeq;
577         IF (c_alldocs%NOTFOUND) THEN
578           CLOSE c_alldocs;
579           EXIT;
580         END IF;
581 
582         -- Make package directories distinct from files.  Note that when
583         -- listing the document recursively, the only packages that are
584         -- listed are the ones which contain no child documents or packages
585         IF ((pathType = 'PACKAGE') AND (pathSeq = -1)) THEN
586           docname := docname || '/';
587         END IF;
588 
589         -- Print the document, but make sure it does not exceed 255 characters
590         -- or else dbms_output will fail
591         WHILE (length(docname) > 255) LOOP
592           dbms_output.put_line(substr(docname, 1, 255));
593           docname := substr(docname, 256);
594         END LOOP;
595         dbms_output.put_line(docname);
596       END LOOP;
597     ELSE
598       dbms_output.put_line('Printing contents of ' || p_path);
599       OPEN c_docs(docID);
600       LOOP
601         FETCH c_docs INTO docname, pathType, pathSeq;
602         IF (c_docs%NOTFOUND) THEN
603           CLOSE c_docs;
604           EXIT;
605         END IF;
606 
607         -- Make package directories distinct from files.
608         IF ((pathType = 'PACKAGE') AND (pathSeq = -1)) THEN
609           docname := docname || '/';
610         END IF;
611 
612         -- Print the document, but make sure it does not exceed 255 characters
613         -- or else dbms_output will fail
614         WHILE (length(docname) > 255) LOOP
615           dbms_output.put_line(substr(docname, 1, 255));
616           docname := substr(docname, 256);
617         END LOOP;
618         dbms_output.put_line(docname);
619       END LOOP;
620     END IF;
621   END;
622 
623 
624   -- List the customizations for the specified document.
625   PROCEDURE listCustomizations(p_document VARCHAR2)
626   IS
627     CURSOR c(pathName VARCHAR2, docName VARCHAR2) IS
628       SELECT jdr_mds_internal.getDocumentName(path_docid)
629       FROM jdr_paths, jdr_attributes
630       WHERE path_docid   = att_comp_docid AND
631             path_name    = pathName       AND
632             att_comp_seq = 0              AND
633             att_name     = 'customizes'   AND
634             att_value    = docName;
635     pathName     JDR_PATHS.PATH_NAME%TYPE;
636     oldCustName  VARCHAR2(1024);
637     startDoc     VARCHAR2(1024);
638     endDoc       VARCHAR2(1024);
639     name         VARCHAR2(1024);
640     lenApp       NUMBER;
641     lenPkg       NUMBER;
642     lenRoot      NUMBER;
643   BEGIN
644     -- First determine the pathName of the base document
645     -- i.e. baseDocName - /oracle/apps/ak/pages/page1
646     --      pathName    - page1
647     lenPkg  := INSTR(p_document, '/', -1, 1);
648     IF lenPkg = 0 OR
649        lenPkg = LENGTH(p_document)
650     THEN
651       RETURN;
652     END IF;
653     pathName := SUBSTR(p_document, lenPkg + 1);
654     OPEN c(pathName, p_document);
655     LOOP
656       FETCH c INTO name;
657       IF (c%NOTFOUND) THEN
658         CLOSE c;
659         EXIT;
660       END IF;
661       dbms_output.put_line(name);
662     END LOOP;
663   END;
664 
665 
666   -- Lists the contents of a package.
667   PROCEDURE listDocuments(p_path VARCHAR2, p_recursive BOOLEAN DEFAULT FALSE)
668   IS
669   BEGIN
670     listContents(p_path, p_recursive);
671   END;
672 
673 
674   -- Lists the supported languages for the specified document.
675   --
676   -- Parameters:
677   --  p_document    - the fully qualified document name, which can represent
678   --                  either a document or package file.
679   --                  (i.e.  '/oracle/apps/ak/attributeSets')
680   --
681   PROCEDURE listLanguages(p_document VARCHAR2)
682   IS
683     CURSOR c_languages(docid jdr_paths.path_docid%TYPE) IS
684       SELECT DISTINCT(atl_lang) FROM jdr_attributes_trans
685       WHERE atl_comp_docid IN (SELECT path_docid FROM jdr_paths
686                                START WITH path_docid = docID
687                                CONNECT BY PRIOR path_docid=path_owner_docid);
688 
689     lang     jdr_attributes_trans.atl_lang%TYPE;
690     docID    jdr_paths.path_docid%TYPE;
691   BEGIN
692     dbms_output.enable(1000000);
693 
694     docID := getDocumentID(p_document, 'FILE');
695 
696     -- Nothing to do if the path does not exist
697     IF (docID = -1) THEN
698       dbms_output.put_line('Error: Could not find document ' || p_document);
699       RETURN;
700     END IF;
701 
702     dbms_output.put_line('Printing languages for document ' || p_document);
703     OPEN c_languages(docID);
704     LOOP
705       FETCH c_languages INTO lang;
706       IF (c_languages%NOTFOUND) THEN
707         CLOSE c_languages;
708         EXIT;
709       END IF;
710 
711       dbms_output.put_line(lang);
712     END LOOP;
713   END;
714 
715 
716   -- Prints the contents of a JRAD document to the console.
717   PROCEDURE printDocument(p_document    VARCHAR2,
718                           p_maxLineSize NUMBER DEFAULT MAX_LINE_SIZE)
719   IS
720     chunk       VARCHAR2(32000);
721     maxLineSize NUMBER := p_maxLineSize;
722   BEGIN
723     dbms_output.enable(1000000);
724 
725     IF (p_maxLineSize > MAX_LINE_SIZE) THEN
726       maxLineSize := MAX_LINE_SIZE;
727     END IF;
728 
729     chunk := jdr_mds_internal.exportDocumentAsXML(p_document);
730 
731     IF chunk IS NULL THEN
732       dbms_output.put_line('Error: Could not find document ' || p_document);
733     ELSE
734       printChunk(chunk, maxLineSize);
735       LOOP
736         chunk := jdr_mds_internal.exportDocumentAsXML(NULL);
737         EXIT WHEN chunk IS NULL;
738         printChunk(chunk, maxLineSize);
739       END LOOP;
740     END IF;
741   END;
742 
743 
744   -- Prints the translations for the document in XLIFF format.
745   PROCEDURE printTranslations(p_document    VARCHAR2,
746                               p_language    VARCHAR2,
747                               p_maxLineSize NUMBER DEFAULT MAX_LINE_SIZE)
748   IS
749     chunk          VARCHAR2(32000);
750     maxLineSize    NUMBER := p_maxLineSize;
751     exportFinished INTEGER;
752   BEGIN
753     dbms_output.enable(1000000);
754 
755     IF (p_maxLineSize > MAX_LINE_SIZE) THEN
756       maxLineSize := MAX_LINE_SIZE;
757     END IF;
758 
759     chunk := jdr_mds_internal.exportXLIFFDocument(exportFinished,
760                                                   p_document,
761                                                   p_language);
762 
763     IF chunk IS NULL THEN
764       dbms_output.put_line('Error: Could not find document ' || p_document);
765     ELSE
766       printChunk(chunk, maxLineSize);
767       WHILE (exportFinished = 0) LOOP
768         chunk := jdr_mds_internal.exportXLIFFDocument(exportFinished,
769                                                       NULL,
770                                                       NULL);
771         IF (chunk IS NOT NULL) THEN
772           printChunk(chunk, maxLineSize);
773         END IF;
774       END LOOP;
775     END IF;
776   END;
777 
778   PROCEDURE saveTranslations(
779     p_document     VARCHAR2,
780     p_translations translationList,
781     p_commit       BOOLEAN := TRUE)
782   IS
783     docID      jdr_paths.path_docid%TYPE;
784     lang       jdr_attributes_trans.atl_lang%TYPE;
785     pos        BINARY_INTEGER;
786     dashpos    BINARY_INTEGER;
787   BEGIN
788     -- Get the document ID for this document
789     docID := getDocumentID(p_document, 'FILE');
790     IF (docID = -1) THEN
791       RAISE no_such_document;
792     END IF;
793 
794     -- Create a savepoint in case of an exception
795     SAVEPOINT saveTranslations_1;
796 
797     -- Lock the document
798     jdr_mds_internal.lockDocument(docID, 100);
799 
800     -- Delete all of the translations
801     DELETE FROM jdr_attributes_trans WHERE atl_comp_docid = docID;
802 
803     -- Insert the new translations
804     FOR pos IN 1..p_translations.COUNT LOOP
805 
806       -- Insure that the language is in the form 'xx-YY'.  That is, the first
807       -- part must be in lower case and the latter part in uppercase.
808       IF (UPPER(NVL(lang, 'INVALID')) <> UPPER(p_translations(pos).lang)) THEN
809         dashpos := INSTR(p_translations(pos).lang, '-');
810         lang := LOWER(SUBSTR(p_translations(pos).lang, 1, dashpos)) ||
811                 UPPER(SUBSTR(p_translations(pos).lang, dashpos + 1));
812       END IF;
813 
814       INSERT
815         INTO jdr_attributes_trans
816           (atl_comp_docid,
817            atl_lang,
818            atl_comp_ref,
819            atl_name,
820            atl_value)
821         VALUES
822           (docID,
823            lang,
824            NVL(p_translations(pos).compref, '.'),
825            p_translations(pos).name,
826            p_translations(pos).value);
827     END LOOP;
828 
829     -- Commit the data (if requested to)
830     IF (p_commit) THEN
831       COMMIT;
832     END IF;
833 
834   EXCEPTION
835     WHEN OTHERS THEN
836       ROLLBACK TO saveTranslations_1;
837       RAISE;
838   END;
839 
840 END;