1 PACKAGE BODY jdr_mds_internal AS
2 /* $Header: JDRMDINB.pls 120.3 2005/09/02 11:38:31 nigoel noship $ */
3 -----------------------------------------------------------------------------
4 ---------------------------- PRIVATE VARIABLES ------------------------------
5 -----------------------------------------------------------------------------
6
7 -- This is used to verify that the repository API and JRAD java code are
8 -- compatible. When the repository API is updated, this constant should
9 -- be modified to match the latest version of JRAD. In addition, in the
10 -- JRAD java code, CompatibleVersions.MIN_REPOS_VERSION may also need to
11 -- be modified.
12 REPOS_VERSION CONSTANT VARCHAR2(32) := '9.0.5.2.0_459';
13
14
15 -- This is used to verify that the repository API and JRAD java code are
16 -- compatible. This is the earliest JRAD version which is compatible
17 -- with the repository.
18 MIN_JRAD_VERSION CONSTANT VARCHAR2(32) := '9.0.3.5.0_226';
19
20 -- NEWLINE character
21 NEWLINE CONSTANT VARCHAR2(1) := '
22 ';
23
24 -- Maximimum size of XML chunk
25 MAX_CHUNK_SIZE CONSTANT INTEGER := 32000;
26
27 -- Indentation for XML elements
28 INDENT_SIZE CONSTANT INTEGER := 3;
29
30 -- Maximum rows to fetch with bulk bind
31 ROWS_TO_FETCH CONSTANT INTEGER := 1000;
32
33 -- Exception raised when MAX_CHUNK_SIZE exceeded
34 chunk_size_exceeded EXCEPTION;
35 document_name_conflict EXCEPTION;
36 corrupt_sequence EXCEPTION;
37
38 -- Cursor to retrieve all of the components of a document
39 CURSOR c_document_contents(docid NUMBER) IS
40 SELECT
41 comp_seq,
42 comp_element,
43 comp_level,
44 comp_grouping,
45 comp_id,
46 comp_ref,
47 comp_extends,
48 comp_use,
49 att_name,
50 att_value
51 FROM
52 jdr_components, jdr_attributes
53 WHERE
54 comp_docid = docid AND
55 comp_docid = att_comp_docid(+) AND
56 comp_seq = att_comp_seq(+)
57 ORDER BY
58 comp_seq,
59 att_comp_seq,
60 att_seq;
61
62 -- Cursor to retrieve all of the components of a package
63 -- bug #(3785730) No need to use RULE hint as it does not help this query.
64 CURSOR c_package_contents(docid NUMBER) IS
65 SELECT
66 path_docid,
67 path_name,
68 path_type,
69 path_seq,
70 path_owner_docid,
71 comp_seq,
72 comp_element,
73 comp_level,
74 comp_grouping,
75 comp_id,
76 comp_ref,
77 comp_extends,
78 comp_use,
79 att_name,
80 att_value
81 FROM
82 jdr_paths, jdr_components, jdr_attributes
83 WHERE
84 path_docid IN
85 (SELECT
86 path_docid
87 FROM
88 jdr_paths
89 START WITH
90 path_docid = docID
91 CONNECT BY PRIOR
92 path_docid=path_owner_docid
93 ) AND
94 path_docid = comp_docid(+) AND
95 comp_docid = att_comp_docid(+) AND
96 comp_seq = att_comp_seq(+)
97 ORDER BY
98 path_seq,
99 comp_seq,
100 att_comp_seq,
101 att_seq;
102
103 -- Cursor to retrieve XLIFF translations
104 -- This query will retrieve the translations for a specified
105 -- document (as well as sub-documents if it's a package file) and
106 -- specified language; and will retrieve the base language as well.
107 CURSOR c_trans(docid jdr_paths.path_docid%TYPE, lang VARCHAR2) IS
108 SELECT
109 atl_comp_docid,
110 atl_comp_ref,
111 atl_name,
112 atl_value
113 FROM
114 jdr_attributes_trans
115 WHERE
116 atl_comp_docid IN (SELECT path_docid FROM jdr_paths
117 START WITH path_docid = docID
118 CONNECT BY PRIOR path_docid=path_owner_docid) AND
119 atl_lang = lang
120 ORDER BY
121 atl_comp_docid,
122 atl_comp_ref;
123
124
125 -- State needed for exportXML
126 TYPE CharArray IS VARRAY(100) OF VARCHAR2(128);
127 TYPE NumArray IS VARRAY(100) OF NUMBER;
128
129 -- Types needed for bulk bind
130 TYPE pathdocidtab IS TABLE OF jdr_paths.path_docid%TYPE;
131 TYPE pathnametab IS TABLE OF jdr_paths.path_name%TYPE;
132 TYPE pathtypetab IS TABLE OF jdr_paths.path_type%TYPE;
133 TYPE pathseqtab IS TABLE OF jdr_paths.path_seq%TYPE;
134 TYPE pathownertab IS TABLE OF jdr_paths.path_owner_docid%TYPE;
135 TYPE compseqtab IS TABLE OF jdr_components.comp_seq%TYPE;
136 TYPE compelementtab IS TABLE OF jdr_components.comp_element%TYPE;
137 TYPE compleveltab IS TABLE OF jdr_components.comp_level%TYPE;
138 TYPE compgroupingtab IS TABLE OF jdr_components.comp_grouping%TYPE;
139 TYPE compidtab IS TABLE OF jdr_components.comp_id%TYPE;
140 TYPE compreftab IS TABLE OF jdr_components.comp_ref%TYPE;
141 TYPE compextendstab IS TABLE OF jdr_components.comp_extends%TYPE;
142 TYPE compusetab IS TABLE OF jdr_components.comp_use%TYPE;
143 TYPE attnametab IS TABLE OF jdr_attributes.att_name%TYPE;
144 TYPE attvaluetab IS TABLE OF jdr_attributes.att_value%TYPE;
145 TYPE varchar64tab IS TABLE OF VARCHAR2(64) INDEX BY BINARY_INTEGER;
146
147 mStack CharArray := NULL;
148 mPackageNames CharArray := NULL;
149 mPackageStack NumArray := NULL;
150 mPackageLevel INTEGER;
151 mPreviousLevel jdr_components.comp_level%TYPE;
152 mPreviousComp jdr_components.comp_seq%TYPE;
153 mPreviousDocID jdr_paths.path_docid%TYPE;
154 mPreviousType jdr_paths.path_type%TYPE;
155 mPartialChunk VARCHAR2(32000);
156 mPartialXLIFFChunk VARCHAR2(32000);
157 mPathType jdr_paths.path_type%TYPE;
158 mIndex INTEGER;
159 mFormatted INTEGER;
160 mFetchComplete BOOLEAN;
161 mPathIds pathdocidtab;
162 mPathNames pathnametab;
163 mPathTypes pathtypetab;
164 mPathSeqs pathseqtab;
165 mPathOwners pathownertab;
166 mCompSeqs compseqtab;
167 mCompElements compelementtab;
168 mCompLevels compleveltab;
169 mCompGroupings compgroupingtab;
170 mCompIds compidtab;
171 mCompRefs compreftab;
172 mCompExtends compextendstab;
173 mCompUses compusetab;
174 mAttNames attnametab;
175 mAttValues attvaluetab;
176
177 -- #(3803543) This was added to reduce the number of SQL executions
178 -- needed to get the document id for a given document name
179 mPackageCache varchar64tab;
180
181 -- User-defined exceptions.
182 -- Each of the following errors should correspond to an error in the
183 -- oracle.jrad.repos.api.DBAccess class.
184 ERROR_BASE CONSTANT INTEGER := -20100;
185 ERROR_DOCUMENT_NAME_CONFLICT CONSTANT INTEGER := ERROR_BASE;
186 ERROR_PACKAGE_NAME_CONFLICT CONSTANT INTEGER := ERROR_BASE - 1;
187 ERROR_CORRUPT_SEQUENCE CONSTANT INTEGER := ERROR_BASE - 2;
188 ERROR_INVALID_NAME CONSTANT INTEGER := ERROR_BASE - 3;
189 ERROR_INCONSISTENT_MAPPING CONSTANT INTEGER := ERROR_BASE - 4;
190 ERROR_ILLEGAL_MAPPING CONSTANT INTEGER := ERROR_BASE - 5;
191
192 -----------------------------------------------------------------------------
193 ----------------------------- PRIVATE FUNCTIONS -----------------------------
194 -----------------------------------------------------------------------------
195
196 --
197 -- Creates the XML for the specified attribute
198 --
199 PROCEDURE addAttribute(
200 newxml IN OUT NOCOPY /* file.sql.39 change */ VARCHAR2,
201 name VARCHAR2,
202 value VARCHAR2)
203 IS
204 BEGIN
205 IF (name IS NOT NULL) THEN
206 newxml := newxml || ' ' || name || '="' || value || '"';
207 END IF;
208 END;
209
210
211 --
212 -- Creates the XML for the new component
213 --
214 PROCEDURE addComponent(
215 newxml IN OUT NOCOPY /* file.sql.39 change */ VARCHAR2,
216 compseq jdr_components.comp_seq%TYPE,
217 compelement jdr_components.comp_element%TYPE,
218 complevel jdr_components.comp_level%TYPE,
219 compgrouping jdr_components.comp_grouping%TYPE,
220 compid jdr_components.comp_id%TYPE,
221 compref jdr_components.comp_ref%TYPE,
222 compextends jdr_components.comp_extends%TYPE,
223 compuse jdr_components.comp_use%TYPE,
224 attname jdr_attributes.att_name%TYPE,
225 attvalue jdr_attributes.att_value%TYPE,
226 formatted BOOLEAN DEFAULT TRUE)
227 IS
228 adjLevel INTEGER;
229 BEGIN
230 --
231 -- If there is a grouping for this component, subtract one from the
232 -- level (as that's the actual starting level of the new component).
233 --
234 IF (compgrouping IS NOT NULL) THEN
235 adjLevel := complevel - 1;
236 ELSE
237 adjLevel := complevel;
238 END IF;
239
240 -- End the previous component (assuming this is not the first component)
241 IF (complevel <> 0) THEN
242 IF (adjLevel <= mPreviousLevel) THEN
243 -- The previous component has no children, so we can end the tag now
244 newxml := newxml || '/>' || NEWLINE;
245 ELSE
246 -- There are potential children to come, so keep the tag open
247 newxml := newxml || '>' || NEWLINE;
248 END IF;
249 END IF;
250
251 -- Check if we need to pop any components/groupings from the stack
252 FOR i IN REVERSE adjLevel+1..mPreviousLevel LOOP
253 IF (formatted) THEN
254 newxml := newxml || rpad(' ', (mPackageLevel+i-1)*INDENT_SIZE, ' ');
255 END IF;
256 newxml := newxml || '</' || mStack(i) || '>' || NEWLINE;
257 END LOOP;
258
259 -- Push the grouping (if it exists)
260 IF (compgrouping IS NOT NULL) THEN
261 mStack(complevel) := compgrouping;
262 IF (formatted) THEN
263 newxml := newxml ||
264 rpad(' ', (mPackageLevel+complevel-1)*INDENT_SIZE, ' ');
265 END IF;
266 newxml := newxml || '<' || compgrouping || '>' || NEWLINE;
267 END IF;
268
269 -- Add the element
270 mStack(complevel+1) := compelement;
271 IF (formatted) THEN
272 newxml := newxml ||
273 rpad(' ', (mPackageLevel+complevel)*INDENT_SIZE, ' ');
274 END IF;
275 newxml := newxml || '<' || compelement;
276
277 -- Add the flat attributes
278 IF (compid IS NOT NULL) THEN
279 newxml := newxml || ' id="' || compid || '"';
280 END IF;
281
282 IF (compref IS NOT NULL) THEN
283 IF (compextends = 'Y') THEN
284 newxml := newxml || ' extends="' || compref || '"';
285 ELSE
286 newxml := newxml || ' ref="' || compref || '"';
287 END IF;
288 END IF;
289
290 IF (compuse IS NOT NULL) THEN
291 newxml := newxml || ' use="' || compuse || '"';
292 END IF;
293
294 -- Add the 4th-normal attribute (if any) from the attributes table
295 addAttribute(newxml, attname, attvalue);
296
297 -- Update the state
298 mPreviousLevel := complevel;
299 mPreviousComp := compseq;
300 END;
301
302
303 --
304 -- Add the XML to the current chunk and raise an exception if the
305 -- maximum size is exceeded.
306 --
307 PROCEDURE addXMLtoChunk(
308 chunk IN OUT NOCOPY /* file.sql.39 change */ VARCHAR2,
309 newxml VARCHAR2)
310 IS
311 BEGIN
312 -- bug #(3955120) Use lengthB to correctly compute the size
313 -- when multibyte characters are used.
314 IF ((LENGTHB(newxml) + LENGTHB(chunk)) > MAX_CHUNK_SIZE) THEN
315 RAISE chunk_size_exceeded;
316 ELSE
317 chunk := chunk || newxml;
318 END IF;
319 END;
320
321
322 --
323 -- Create the XML header for the given document ID
324 --
325 FUNCTION addXMLHeader(
326 docID JDR_PATHS.PATH_DOCID%TYPE) RETURN VARCHAR2
327 IS
328 xml_version jdr_paths.path_xml_version%TYPE;
329 xml_encoding jdr_paths.path_xml_encoding%TYPE;
330 BEGIN
331 -- Get the version and encoding
332 SELECT
333 path_xml_version, path_xml_encoding
334 INTO
335 xml_version, xml_encoding
336 FROM
337 jdr_paths
338 WHERE
339 path_docid = docID;
340
341 -- ###
342 -- ### #(2424399) Need to be able to retrieve XML_ENCODING and XML_VERSION
343 -- ### from the XML file
344 -- ###
345 IF (xml_encoding IS NULL) THEN
346 xml_encoding := 'UTF-8';
347 END IF;
348 IF (xml_version IS NULL) THEN
349 xml_version := '1.0';
350 END IF;
351
352 -- Return the xml header
356
353 RETURN ('<?xml version=''' || xml_version || ''' encoding=''' ||
354 xml_encoding || '''?>' || NEWLINE);
355 END;
357
358 --
359 -- Retrieves the document id for the specified fully qualified path name.
360 -- The pathname must begin with a '/' and should look something like:
361 -- /oracle/apps/AK/mydocument
362 --
363 -- Parameters:
364 -- fullPathName - the fully qualified name of the document
365 --
366 -- allowChildDoc - a non-zero value indicates that "child" documents
367 -- are allowed, where child documents are documents
368 -- which exist as part of a package document
369 --
370 -- includePackage - a non-zero value indicates that, if this is a "child"
371 -- document and it allowChildDoc is TRUE, then the docid
372 -- of the package document will be returned; otherwise,
373 -- the ID of the "child" document will be returned
374 --
375 -- Returns:
376 -- Returns the ID of the path or -1 if no such path exists
377 --
378 FUNCTION getDocumentID(
379 fullPathName VARCHAR2,
380 allowChildDoc BOOLEAN,
381 includePackage BOOLEAN) RETURN NUMBER
382 IS
383 packageName VARCHAR2(512);
384 pName JDR_PATHS.PATH_NAME%TYPE;
385 pType JDR_PATHS.PATH_TYPE%TYPE;
386 pSeq JDR_PATHS.PATH_SEQ%TYPE;
387 ownerID JDR_PATHS.PATH_OWNER_DOCID%TYPE := 0;
388 docID JDR_PATHS.PATH_DOCID%TYPE := -1;
389 lastSlashPos BINARY_INTEGER;
390 BEGIN
391 packageName := fullPathName;
392
393 -- #(3403125) Remove the trailing forward slash
394 IF (INSTR(packageName, '/', LENGTH(packageName)) > 0) THEN
395 packageName := SUBSTR(packageName, 1, LENGTH(packageName) - 1);
396 END IF;
397
398 -- Separate the leaf path name and package name
399 lastSlashPos := INSTR(packageName, '/', -1);
400 pName := SUBSTR(packageName, lastSlashPos + 1);
401 packageName := SUBSTR(packageName, 1, lastSlashPos - 1);
402
403 -- Get the id for the package
404 ownerID := getDocumentID(packageName, 'PACKAGE');
405 IF ownerID = -1 THEN
406 RETURN (-1);
407 END IF;
408
409 -- Now get the ID for the document
410 SELECT path_docid, path_type, path_seq
411 INTO docID, pType, pSeq
412 FROM jdr_paths
413 WHERE path_name = pName AND path_owner_docid = ownerID;
414
415 IF (pType = 'PACKAGE') THEN
416 IF (pSeq = 0) THEN
417 -- This is a package document
418 RETURN (docID);
419 END IF;
420 ELSIF (pType = 'DOCUMENT') THEN
421 IF (pSeq = -1) THEN
422 -- This is a document which is not apart of a package document
423 RETURN (docID);
424 ELSIF (allowChildDoc) THEN
425 --
426 -- This is a child document, so we need to return either the
427 -- ID of the package document, or the ID of the child document.
428 --
429 IF (includePackage) THEN
430 RETURN (getPackageDocument(docID));
431 ELSE
432 RETURN (docID);
433 END IF;
434 END IF;
435 END IF;
436
437 RETURN (-1);
438 EXCEPTION
439 WHEN NO_DATA_FOUND THEN
440 RETURN (-1);
441 END;
442
443
444 -----------------------------------------------------------------------------
445 ----------------------------- PUBLIC FUNCTIONS ------------------------------
446 -----------------------------------------------------------------------------
447
448
449 --
450 -- Creates an entry in the jdr_paths table for the document or package.
451 -- The full name of the document/package must be specified. Any packages
452 -- which do not already exist will be created as well.
456 -- fullPathName - the complete path name of the document or package
453 --
454 -- Parameters:
455 -- username - user who is creating the document
457 -- docType - either 'PACKAGE' or 'DOCUMENT' OR NULL
458 -- xmlversion - xml version
459 -- xmlencoding - xml encoding
460 --
461 -- Returns:
462 -- the ID of the created path
463 --
464 FUNCTION createPath(
465 username VARCHAR2,
466 fullPathName VARCHAR2,
467 docType VARCHAR2,
468 xmlversion VARCHAR2,
469 xmlencoding VARCHAR2) RETURN NUMBER
470 IS
471 CURSOR c(packageName VARCHAR2, ownerID NUMBER) IS
472 SELECT path_docid
473 FROM jdr_paths
474 WHERE path_name = packageName AND
475 path_owner_docid = ownerID AND
476 path_seq = -1 AND
477 path_type = 'PACKAGE';
478
479 ownerID jdr_paths.path_docid%TYPE := 0;
480 newDocID jdr_paths.path_docid%TYPE := 0;
481 docID jdr_paths.path_docid%TYPE := 0;
482 pathSeq jdr_paths.path_seq%TYPE := 0;
483 packageName jdr_paths.path_name%TYPE;
484 slashpos INTEGER := 1;
485 tempdoc VARCHAR2(1024) := fullPathName;
486 BEGIN
487
488 -- Skip the first slash
489 IF (INSTR(tempdoc, '/') <> 1) THEN
490 RETURN (-1);
491 ELSE
492 tempdoc := SUBSTR(tempdoc, 2);
493 END IF;
494
495 -- #(3403125) Remove the trailing forward slash
496 IF (INSTR(tempdoc, '/', LENGTH(tempdoc)) > 0) THEN
497 tempdoc := SUBSTR(tempdoc, 1, LENGTH(tempdoc) - 1);
498 END IF;
499
500 WHILE (slashpos <> 0) LOOP
501
502 -- Search for the next slash
503 slashpos := INSTR(tempdoc, '/');
504
505 -- A null docType indicates that this path represents a directory.
506 -- If so, do not create an entry for the package document or document.
507 IF ((slashpos = 0) AND (docType IS NOT NULL)) THEN
508 --
509 -- There are no more slashes, which means that all that is left
510 -- is the name of the package document (pathSeq = 0) or the name of
511 -- the document (pathSeq=-1).
512 --
513 IF (docType = 'PACKAGE') THEN
514 pathSeq := 0;
515 ELSE
516 pathSeq := -1;
517 END IF;
518 docID := createPath(username,
519 tempdoc,
520 ownerID,
521 pathSeq,
522 docType,
523 xmlversion,
524 xmlencoding);
525 ELSE
526 -- Get the package name
527 IF (slashpos <> 0) THEN
528 packageName := SUBSTR(tempdoc, 1, slashpos-1);
529 tempdoc := SUBSTR(tempdoc, slashpos+1);
530 ELSE
531 -- This will happen if this is the last part of the path and the
532 -- path represents a directory (i.e. docType is null)
533 packageName := tempdoc;
534 END IF;
535
536 -- Does this package already exist
537 OPEN c(packageName, ownerID);
538 FETCH c INTO newDocID;
539
540 -- Insert the package if it does not already exist
541 IF c%NOTFOUND THEN
542 newDocID := createPath(username, packageName, ownerID, -1, 'PACKAGE');
543 END IF;
544 CLOSE c;
545
546 ownerID := newDocID;
547
548 -- If this is the last part of the path (i.e. slashpos = 0), then
549 -- this path represents a directory and we are done
550 IF (slashpos = 0) THEN
551 docID := ownerID;
552 END IF;
553
554 END IF;
555 END LOOP;
556
557 RETURN (docID);
558 END;
559
560
561 --
562 -- Creates an entry in the jdr_paths document.
563 --
564 -- Parameters:
565 -- username - user who is creating the document
566 -- pathname - the name of the document/package (not fully qualified)
567 -- ownerID - the ID of the owning package
568 -- pathSeq - sequence of the path
569 -- docType - either 'DOCUMENT' or 'PACKAGE'
570 -- xmlversion - xml version, which can be null for "child" documents
571 -- xmlencoding - xml encoding, which can be null for "child" documents
572
573 --
574 -- Returns:
575 -- the ID of the created path
576 --
577 FUNCTION createPath(
578 username VARCHAR2,
579 pathname VARCHAR2,
580 ownerID JDR_PATHS.PATH_OWNER_DOCID%TYPE,
581 pathSeq JDR_PATHS.PATH_SEQ%TYPE,
582 docType VARCHAR2,
583 xmlversion VARCHAR2 DEFAULT NULL,
584 xmlencoding VARCHAR2 DEFAULT NULL) RETURN NUMBER
585 IS
586 docID JDR_PATHS.PATH_DOCID%TYPE;
587 BEGIN
588 -- Get the next document ID
589 SELECT jdr_document_id_s.NEXTVAL INTO docID FROM DUAL;
590
591 INSERT INTO jdr_paths
592 (PATH_NAME, PATH_DOCID, PATH_OWNER_DOCID, PATH_TYPE, PATH_SEQ,
593 PATH_XML_VERSION, PATH_XML_ENCODING,
594 CREATED_BY, CREATION_DATE, LAST_UPDATED_BY, LAST_UPDATE_DATE,
595 LAST_UPDATE_LOGIN)
596 VALUES
600
597 (pathname, docID, ownerID, docType, pathSeq,
598 xmlversion, xmlencoding,
599 username, SYSDATE, username, SYSDATE, username);
601 RETURN (docID);
602 EXCEPTION
603 -- An exception can be caused by one of the following situations:
604 -- (1) If the sequence JDR_DOCUMENT_ID_S is corrupt (i.e. the current value
605 -- of the sequence is less than the maximum document ID)
606 -- (2) We are trying to insert a document whose name matches an existing
607 -- package or vice versa. For example, suppose we have a document
608 -- called:
609 -- /demo/test/mydoc.xml
610 -- and we try to create a document called:
611 -- /demo/test.xml
612 -- This will fail due to the unique index on (path_owner_docid,
613 -- path_name).
614 -- (3) Two users are attempting to insert the same document at the same
615 -- time
616 WHEN DUP_VAL_ON_INDEX THEN
617 -- If this exception was caused by (3), then the following select should
618 -- now return the correct docid because the first user will have
619 -- finished saving the document.
620 DECLARE
621 cnt INTEGER;
622 BEGIN
623 SELECT path_docid INTO docID
624 FROM jdr_paths
625 WHERE
626 path_name = pathname AND
627 path_owner_docid = ownerID AND
628 path_type = docType AND
629 path_seq = pathseq;
630 RETURN (docID);
631 EXCEPTION
632 -- Since no data was found, we know this was caused by either (1) or
633 -- or (2). If the following query returns no rows, then this can only
634 -- be explained by a corrupt sequence; otherwise, we are dealing with
635 -- a name conflict.
636 WHEN NO_DATA_FOUND THEN
637 SELECT count(*) INTO cnt
638 FROM jdr_paths
639 WHERE
640 path_name = pathname AND
641 path_owner_docid = ownerID;
642
643 IF (cnt = 0) THEN
644 raise corrupt_sequence;
645 ELSE
646 raise document_name_conflict;
647 END IF;
648 END;
649 END;
650
651
652 --
653 -- Delete the document.
654 --
655 -- Parameters:
656 -- docID - ID of the document to delete
657 -- isDrop - should the document be dropped as well. If TRUE, then the
658 -- document will be completely removed. If FALSE, then only
659 -- the contents of the document will be deleted, and the entry
660 -- in the jdr_paths table will remain.
661 --
662 -- Notes:
663 -- If the document is a package document, then the "child" documents will
664 -- be deleted as well.
665 --
666 PROCEDURE deleteDocument(
667 docID JDR_PATHS.PATH_DOCID%TYPE,
668 isDrop BOOLEAN DEFAULT FALSE)
669 IS
670 BEGIN
671 -- Delete the attributes of the document
672 DELETE jdr_attributes WHERE att_comp_docid IN
673 (SELECT path_docid FROM jdr_paths
674 START WITH path_docid = docid
675 CONNECT BY PRIOR path_docid=path_owner_docid);
676
677 -- Delete the components of the document
678 DELETE jdr_components WHERE comp_docid IN
679 (SELECT path_docid FROM jdr_paths
680 START WITH path_docid = docid
681 CONNECT BY PRIOR path_docid=path_owner_docid);
682
683 --
684 -- If isDrop is TRUE, then delete the document from the jdr_paths
685 -- table. Otherwise, mark the path_seq to a special value (-2).
686 --
687 -- We have to go through this complexity because:
688 -- (1) When updating a document, we want to make sure that the
689 -- document ID is preserved. As such, we can not simply remove
690 -- the row from the jdr_paths table and create a new one later.
691 --
692 -- (2) However, there could be a scenario where a package document that
693 -- is originally in the package, is removed at some later time. In
694 -- order to insure that these "dangling" packages get deleted
695 -- properly, we mark the path_seq to -2, and, if after saving the
696 -- document, the path_seq has not been changed to a postive integer,
697 -- then we know the document should be removed.
698 --
699 IF (isDrop) THEN
700 DELETE jdr_paths where path_docid IN
701 (SELECT path_docid FROM jdr_paths
702 START WITH path_docid = docid
703 CONNECT BY PRIOR path_docid=path_owner_docid);
704 ELSE
705 UPDATE jdr_paths SET path_seq = -2
706 WHERE path_seq > 0 AND path_docid IN
707 (SELECT path_docid FROM jdr_paths
708 START WITH path_docid = docid
709 CONNECT BY PRIOR path_docid=path_owner_docid);
710 END IF;
711
712 -- ###
713 -- ### Do we need to deal with references, translations or customizations.
714 -- ###
715 END;
716
717
718 --
719 -- Drops the document and the document's contents from the repository.
720 -- If the document is a package document, the "child" documents of the
721 -- package document will be dropped as well.
722 --
723 PROCEDURE dropDocument(
724 docID JDR_PATHS.PATH_DOCID%TYPE)
725 IS
726 BEGIN
727 -- ###
731 END;
728 -- ### Should we allow the dropping of child documents and/or packages?
729 -- ###
730 jdr_mds_internal.deleteDocument(docID, TRUE);
732
733
734 --
735 -- Export the XML for a "single" document and pass it back in 32k chunks.
736 -- This function will return XML chunks, with a maximum size of 32k.
737 --
738 -- A "single" document is simply a document which is not a package document.
739 -- See comments for exportDocumentAsXML for more information.
740 --
741 FUNCTION exportSingleDocument(
742 docID JDR_PATHS.PATH_DOCID%TYPE,
743 iFormatted INTEGER,
744 exportFinished OUT NOCOPY /* file.sql.39 change */ INTEGER) RETURN VARCHAR2
745 IS
746 chunk VARCHAR2(32000);
747 newxml VARCHAR2(32000);
748 formatted BOOLEAN := (iFormatted = 1);
749 BEGIN
750 -- Assume that the document will fit in this 32k chunk
751 exportFinished := 1;
752
753 --
754 -- This procedure returns the XML for the specified document. Since the
755 -- XML can be potentially large (greater than 32k) and since, for
756 -- performance reasons, we do not want to return more than 32k at a time,
757 -- this procedure may need to be called multiple times to retrieve the
758 -- entire document. As such, the "state" of the export is stored in
759 -- package variables.
760 --
761 -- A non-null document indicates that the export process is just
762 -- being started, so let's do the necessary initialization.
763 --
764 IF (docID IS NOT NULL) THEN
765 -- Get the XML header
766 chunk := addXMLHeader(docID);
767
768 -- Initialize the state of the export
769 mPackageLevel := 0;
770 mPreviousDocID := -1;
771 mPreviousComp := -1;
772 mPreviousLevel := -1;
773 mPreviousType := NULL;
774 mPartialChunk := '';
775 mIndex := -1;
776 mFetchComplete := FALSE;
777
778 -- Verify that the cursor is closed
779 IF (c_document_contents%ISOPEN) THEN
780 -- ###
781 -- ### Not sure what to do here, as the cursor should never be open
782 -- ### For now, we'll just close it and cross our fingers
783 -- ###
784 CLOSE c_document_contents;
785 END IF;
786
787 -- And open the cursor that will retrieve the documents/packages
788 OPEN c_document_contents(docID);
789
790 ELSIF (mPartialChunk IS NULL) THEN
791 -- We have finished exporting the document, just return NULL to
792 -- indicate that there is no more XML for this document
793 RETURN (NULL);
794 ELSE
795 -- Get the leftovers (if any) from the previous call to this function
796 chunk := mPartialChunk;
797 mPartialChunk := '';
798 END IF;
799
800 newxml := NULL;
801 IF (c_document_contents%ISOPEN) THEN
802 <<get_components_loop>>
803 LOOP
804 --
805 -- Retrieve the next set of rows if we are currently not in the
806 -- middle of processing a fetched set or rows.
807 --
808 IF (mIndex = -1) THEN
809 -- #(2995144) Check if there are any more rows to fetch
810 IF (mFetchComplete) THEN
811 CLOSE c_document_contents;
812 EXIT;
813 END IF;
814
815 -- Fetch the next set of rows
816 FETCH c_document_contents BULK COLLECT
817 INTO mCompSeqs, mCompElements, mCompLevels, mCompGroupings,
818 mCompIds, mCompRefs, mCompExtends, mCompUses,
819 mAttNames, mAttValues
820 LIMIT ROWS_TO_FETCH;
821
822 -- Since we are only fetching records if either (1) this is the first
823 -- fetch or (2) the previous fetch did not retrieve all of the
824 -- records, then at least one row should always be fetched. But
825 -- checking just to make sure.
826 IF (c_document_contents%ROWCOUNT = 0) THEN
827 CLOSE c_document_contents;
828 EXIT;
829 END IF;
830
831 -- Check if all of the rows have been fetched. If so, indicate that
832 -- the fetch is complete so that another fetch is not made.
833 IF (c_document_contents%NOTFOUND) THEN
834 mFetchComplete := TRUE;
835 END IF;
836
837 mIndex := mCompSeqs.FIRST;
838 END IF;
839
840 <<add_components_loop>>
841 WHILE (mIndex <= mCompSeqs.COUNT) LOOP
842 IF (mCompSeqs(mIndex) <> mPreviousComp) THEN
843
844 -- We are starting a new component, so add the "previous" component
845 addXMLtoChunk(chunk, newxml);
846 newxml := NULL;
847
848 -- And start building the new component
849 addComponent(newxml,
850 mCompSeqs(mIndex), mCompElements(mIndex),
851 mCompLevels(mIndex), mCompGroupings(mIndex),
852 mCompIds(mIndex), mCompRefs(mIndex),
853 mCompExtends(mIndex), mCompUses(mIndex),
854 mAttNames(mIndex), mAttValues(mIndex),
855 formatted);
856 ELSE
857 --
858 -- This is the same sequence of the previous row, which means
862 END IF;
859 -- it's only a new attribute, so just add the XML for the attribute
860 --
861 addAttribute(newxml, mAttNames(mIndex), mAttValues(mIndex));
863
864 -- Increment the index to get the next row
865 mIndex := mIndex + 1;
866 END LOOP add_components_loop;
867
868 -- We are not in the middle of processing a bulk fetch anymore
869 mIndex := -1;
870
871 -- Append any leftover XML
872 addXMLtoChunk(chunk, newxml);
873 newxml := NULL;
874
875 END LOOP get_components_loop;
876
877 --
878 -- We have finished exporting the document. The only task that remains
879 -- it to end the previous component and to unwind the stack
880 --
881 newxml := NULL;
882
883 -- End the previous element
884 newxml := newxml || '/>' || NEWLINE;
885
886 -- Unwind the document stack
887 WHILE (mPreviousLevel > 0) LOOP
888 IF (formatted) THEN
889 newxml := newxml ||
890 rpad(' ', (mPreviousLevel-1)*INDENT_SIZE, ' ');
891 END IF;
892 newxml := newxml || '</' || mStack(mPreviousLevel) || '>' || NEWLINE;
893 mPreviousLevel := mPreviousLevel - 1;
894 END LOOP;
895 addXMLtoChunk(chunk, newxml);
896 END IF;
897
898 --
899 -- Return the current chunk, and set the mPartialChunk to NULL so that,
900 -- when entering this function again, we will know that we have finished
901 -- processing the document.
902 --
903 mPartialChunk := NULL;
904 RETURN (chunk);
905
906 EXCEPTION
907 WHEN chunk_size_exceeded THEN
908 exportFinished := 0;
909 mPartialChunk := newxml;
910 RETURN (chunk);
911 END;
912
913
914 --
915 -- Export the XML for a package document and pass it back in 32k chunks.
916 -- This function will return XML chunks, with a maximum size of 32k.
917 --
918 -- See comments for exportDocumentAsXML for more information.
919 --
920 FUNCTION exportPackageDocument(
921 docID JDR_PATHS.PATH_DOCID%TYPE,
922 iFormatted INTEGER DEFAULT 1,
923 exportFinished OUT NOCOPY /* file.sql.39 change */ INTEGER) RETURN VARCHAR2
924 IS
925 chunk VARCHAR2(32000);
926 newxml VARCHAR2(32000);
927 formatted BOOLEAN := (iFormatted = 1);
928 BEGIN
929 -- Assume that the document will fit in this 32k chunk
930 exportFinished := 1;
931
932 --
933 -- This procedure returns the XML for the specified document. Since the
934 -- XML can be potentially large (greater than 32k) and since, for
935 -- performance reasons, we do not want to return more than 32k at a time,
936 -- this procedure may need to be called multiple times to retrieve the
937 -- entire document. As such, the "state" of the export is stored in
938 -- package variables.
939 --
940 -- A non-null document indicates that the export process is just
941 -- being started, so let's do the necessary initialization.
942 --
943 IF (docID IS NOT NULL) THEN
944 chunk := addXMLHeader(docID);
945
946 -- Initialize the state of the export
947 mPackageLevel := 0;
948 mPreviousDocID := -1;
949 mPreviousComp := -1;
950 mPreviousLevel := -1;
951 mPreviousType := NULL;
952 mPartialChunk := '';
953 mIndex := -1;
954 mFetchComplete := FALSE;
955
956 -- Verify that the cursor is closed
957 IF (c_package_contents%ISOPEN) THEN
958 -- ###
959 -- ### Not sure what to do here, as the cursor should never be open
960 -- ### For now, we'll just close it and cross our fingers
961 -- ###
962 CLOSE c_package_contents;
963 END IF;
964
965 -- And open the cursor that will retrieve contents of the package
966 OPEN c_package_contents(docID);
967
968 ELSIF (mPartialChunk IS NULL) THEN
969 -- We have finished exporting the document, just return NULL to
970 -- indicate that there is no more XML for this document
971 RETURN (NULL);
972 ELSE
973 -- Get the leftovers (if any) from the previous call to this function
974 chunk := mPartialChunk;
975 mPartialChunk := '';
976 END IF;
977
978 newxml := NULL;
979 IF (c_package_contents%ISOPEN) THEN
980 <<get_documents_loop>>
981 LOOP
982 --
983 -- Retrieve the next set of rows if we are currently not in the
984 -- middle of processing a fetched set or rows.
985 --
986 IF (mIndex = -1) THEN
987 -- #(2995144) Check if there are any more rows to fetch
988 IF (mFetchComplete) THEN
989 CLOSE c_package_contents;
990 EXIT;
991 END IF;
992
993 -- Get the next set of rows
994 FETCH c_package_contents BULK COLLECT
995 INTO mPathIds, mPathNames, mPathTypes, mPathSeqs, mPathOwners,
996 mCompSeqs, mCompElements, mCompLevels, mCompGroupings,
997 mCompIds, mCompRefs, mCompExtends, mCompUses,
998 mAttNames, mAttValues
999 LIMIT ROWS_TO_FETCH;
1000
1004 -- checking just to make sure.
1001 -- Since we are only fetching records if either (1) this is the first
1002 -- fetch or (2) the previous fetch did not retrieve all of the
1003 -- records, then at least one row should always be fetched. But
1005 IF (c_package_contents%ROWCOUNT = 0) THEN
1006 CLOSE c_package_contents;
1007 EXIT;
1008 END IF;
1009
1010 -- Check if all of the rows have been fetched. If so, indicate that
1011 -- the fetch is complete so that another fetch is not made.
1012 IF (c_package_contents%NOTFOUND) THEN
1013 mFetchComplete := TRUE;
1014 END IF;
1015
1016 mIndex := mPathIds.FIRST;
1017 END IF;
1018
1019 <<get_documents_loop>>
1020 WHILE (mIndex <= mPathIds.COUNT) LOOP
1021 IF (mPathIds(mIndex) = mPreviousDocID) THEN
1022 IF (mCompSeqs(mIndex) = mPreviousComp) THEN
1023 --
1024 -- If this is the same sequence of the previous row, then it's
1025 -- only a new attribute, so just add the XML for the attribute
1026 --
1027 addAttribute(newxml, mAttNames(mIndex), mAttValues(mIndex));
1028 ELSE
1029 --
1030 -- This is a different sequence from the previous row, which means
1031 -- it's a different component, so add the XML for the new component
1032 --
1033 addComponent(newxml,
1034 mCompSeqs(mIndex), mCompElements(mIndex),
1035 mCompLevels(mIndex), mCompGroupings(mIndex),
1036 mCompIds(mIndex), mCompRefs(mIndex),
1037 mCompExtends(mIndex), mCompUses(mIndex),
1038 mAttNames(mIndex), mAttValues(mIndex),
1039 formatted);
1040 END IF;
1041 ELSE
1042 --
1043 -- This is a new document or package, which means we have to take
1044 -- care of the following actions:
1045 --
1046 -- (1) Finish the previous document or package
1047 -- (2) Unwind the package stack if necessary
1048 -- (3) Reset the state for a new document
1049 --
1050
1051 -- End the previous element
1052 IF (mPreviousType = 'DOCUMENT') THEN
1053 newxml := newxml || '/>' || NEWLINE;
1054 ELSIF (mPreviousType = 'PACKAGE') THEN
1055 newxml := newxml || '>' || NEWLINE;
1056 END IF;
1057
1058 -- Unwind the document stack
1059 WHILE (mPreviousLevel > 0) LOOP
1060 IF (formatted) THEN
1061 newxml := newxml ||
1062 rpad(' ', (mPackageLevel+mPreviousLevel-1)*INDENT_SIZE, ' ');
1063 END IF;
1064 newxml := newxml || '</' || mStack(mPreviousLevel) || '>' || NEWLINE;
1065 mPreviousLevel := mPreviousLevel - 1;
1066 END LOOP;
1067
1068 -- Unwind the package stack
1069 IF (mPackageLevel > 0) THEN
1070 WHILE (mPathOwners(mIndex) <> mPackageStack(mPackageLevel)) LOOP
1071 IF (formatted) THEN
1072 newxml := newxml || rpad(' ', (mPackageLevel-1)*INDENT_SIZE, ' ');
1073 END IF;
1074 newxml := newxml || '</package>' || NEWLINE;
1075 mPackageLevel := mPackageLevel - 1;
1076 END LOOP;
1077 END IF;
1078
1079 -- Reset the state for this new document
1080 mPreviousDocID := mPathIds(mIndex);
1081 mPreviousComp := mCompSeqs(mIndex);
1082 mPreviousLevel := mCompLevels(mIndex);
1083 mPreviousType := mPathTypes(mIndex);
1084
1085 IF (mPathTypes(mIndex) = 'DOCUMENT') THEN
1086 -- If it's a document, add the first component
1087 addComponent(newxml,
1088 mCompSeqs(mIndex), mCompElements(mIndex),
1089 mCompLevels(mIndex), mCompGroupings(mIndex),
1090 mCompIds(mIndex), mCompRefs(mIndex),
1091 mCompExtends(mIndex), mCompUses(mIndex),
1092 mAttNames(mIndex), mAttValues(mIndex),
1093 formatted);
1094 ELSIF (mPathTypes(mIndex) = 'PACKAGE') THEN
1095 IF (mPathSeqs(mIndex) = 0) THEN
1096 --
1097 -- This is the first package of a package file. As such, it
1098 -- does not have a packageName attribute.
1099 --
1100 newxml := newxml || '<' || mCompElements(mIndex);
1101 ELSE
1102 IF (formatted) THEN
1103 newxml := newxml || rpad(' ', (mPackageLevel)*INDENT_SIZE, ' ');
1104 END IF;
1105 newxml := newxml || '<' || mCompElements(mIndex) || ' packageName="' || mPathNames(mIndex) || '"';
1106 END IF;
1107
1108 -- Add any attributes, although only the top-level package should
1109 -- have attributes.
1110 addAttribute(newxml, mAttNames(mIndex), mAttValues(mIndex));
1111
1112 -- Add the package to the package stack
1113 mPackageLevel := mPackageLevel + 1;
1117 END IF;
1114 mPackageStack(mPackageLevel) := mPathIds(mIndex);
1115 mPackageNames(mPackageLevel) := mCompElements(mIndex);
1116 END IF;
1118
1119 -- Increment the index to get the next row
1120 mIndex := mIndex + 1;
1121
1122 -- Add the newxml to the chunk
1123 addXMLtoChunk(chunk, newxml);
1124 newxml := NULL;
1125
1126 END LOOP add_documents_loop;
1127
1128 -- We are not in the middle of processing a bulk fetch anymore
1129 mIndex := -1;
1130
1131 END LOOP get_documents_loop;
1132
1133 --
1134 -- We have finished exporting the document. The only task that remains
1135 -- it to end the previous component and to unwind the document stack
1136 -- and package stack.
1137 --
1138 newxml := NULL;
1139
1140 -- End the previous element
1141 IF (mPreviousType = 'DOCUMENT') THEN
1142 newxml := newxml || '/>' || NEWLINE;
1143 ELSIF (mPreviousType = 'PACKAGE') THEN
1144 newxml := newxml || '>' || NEWLINE;
1145 END IF;
1146
1147 -- Unwind the document stack
1148 WHILE (mPreviousLevel > 0) LOOP
1149 IF (formatted) THEN
1150 newxml := newxml ||
1151 rpad(' ', (mPackageLevel+mPreviousLevel-1)*INDENT_SIZE, ' ');
1152 END IF;
1153 newxml := newxml || '</' || mStack(mPreviousLevel) || '>' || NEWLINE;
1154 mPreviousLevel := mPreviousLevel - 1;
1155 END LOOP;
1156
1157 -- Unwind the package stack
1158 WHILE (mPackageLevel > 0) LOOP
1159 IF (formatted) THEN
1160 newxml := newxml || rpad(' ', (mPackageLevel-1)*INDENT_SIZE, ' ');
1161 END IF;
1162 newxml := newxml || '</' || mPackageNames(mPackageLevel) || '>' || NEWLINE;
1163 mPackageLevel := mPackageLevel - 1;
1164 END LOOP;
1165 addXMLtoChunk(chunk, newxml);
1166 END IF;
1167
1168 --
1169 -- Return the current chunk, and set the mPartialChunk to NULL so that,
1170 -- when entering this function again, we will know that we have finished
1171 -- processing the document.
1172 --
1173 mPartialChunk := NULL;
1174 RETURN (chunk);
1175
1176 EXCEPTION
1177 WHEN chunk_size_exceeded THEN
1178 exportFinished := 0;
1179 mPartialChunk := newxml;
1180 RETURN (chunk);
1181 END;
1182
1183
1184 --
1185 -- This method has been deprecated. Please use the function which
1186 -- has a return value which indicates whether or not the export is
1187 -- complete.
1188 --
1189 -- Export the XML for a document and pass it back in 32k chunks. This
1190 -- function will return XML chunks, with a maximum size of 32k. When
1191 -- the entire document has been exported, this function will return NULL.
1192 --
1193 -- Specifying a document name will initiate the export. Thereafter, a NULL
1194 -- document name should be passed in until the export is complete.
1195 -- That is, to export an entire document, you should do:
1196 --
1197 -- firstChunk := jdr_mds_internal.exportDocumentAsXML('/oracle/apps/fnd/mydoc');
1198 -- LOOP
1199 -- nextChunk := jdr_mds_internal.exportDocumentAsXML(NULL);
1200 -- EXIT WHEN nextChunk IS NULL;
1201 -- END LOOP;
1202 --
1203 -- Parameters:
1204 -- fullName - the fully qualifued name of the document. however,
1205 -- after the first chunk of text is exported, a NULL value
1206 -- should be passed in.
1207 --
1208 -- formatted- a non-zero value indicates that the XML is formatted nicely
1209 -- (i.e. whether or not the elements are indented)
1210 --
1211 -- allowChildDoc - a non-zero value indicates that "child" documents
1212 -- can be exported, where child documents are documents
1213 -- which exist as part of a package document
1214 --
1215 -- includePackage - a non-zero value indicates that, if this is a "child"
1216 -- document and it allowChildDoc is TRUE, then the entire
1217 -- package document should be exported; otherwise, only
1218 -- the XML for the child document will be exported.
1219 --
1220 -- Returns:
1221 -- The exported XML, in 32k chunks.
1222 --
1223 FUNCTION exportDocumentAsXML(
1224 fullName VARCHAR2,
1225 formatted INTEGER DEFAULT 1,
1226 allowChildDoc INTEGER DEFAULT 0,
1227 includePackage INTEGER DEFAULT 0) RETURN VARCHAR2
1228 IS
1229 exportFinished INTEGER;
1230 BEGIN
1231 return (exportDocumentAsXML(exportFinished,
1232 fullName,
1233 formatted,
1234 includePackage));
1235 END;
1236
1237
1238 --
1239 -- Export the XML for a document and pass it back in 32k chunks. This
1240 -- function will return XML chunks, with a maximum size of 32k.
1241 --
1242 -- Specifying a document name will initiate the export. Thereafter, a NULL
1243 -- document name should be passed in until the export is complete.
1244 -- That is, to export an entire document, you should do:
1245 --
1246 -- firstChunk := jdr_mds_internal.exportDocumentAsXML(isDone,
1250 -- END LOOP;
1247 -- '/oracle/apps/fnd/mydoc');
1248 -- WHILE (isDone = 0)
1249 -- nextChunk := jdr_mds_internal.exportDocumentAsXML(isDone, NULL);
1251 --
1252 -- Parameters:
1253 -- exportFinished - OUT NOCOPY /* file.sql.39 change */ parameter which indicates whether or not the export
1254 -- is complete. 1 indicates the entire document is
1255 -- exported, 0 indicates that there are more chunks
1256 -- remaining.
1257 --
1258 -- fullName - the fully qualifued name of the document. however,
1259 -- after the first chunk of text is exported, a NULL value
1260 -- should be passed in.
1261 --
1262 -- formatted - a non-zero value indicates that the XML is formatted nicely
1263 -- (i.e. whether or not the elements are indented)
1264 --
1265 -- allowChildDoc - a non-zero value indicates that "child" documents
1266 -- can be exported, where child documents are documents
1267 -- which exist as part of a package document
1268 --
1269 -- includePackage - a non-zero value indicates that, if this is a "child"
1270 -- document and it allowChildDoc is TRUE, then the entire
1271 -- package document should be exported; otherwise, only
1272 -- the XML for the child document will be exported.
1273 --
1274 -- Returns:
1275 -- The exported XML, in 32k chunks.
1276 --
1277 FUNCTION exportDocumentAsXML(
1278 exportFinished OUT NOCOPY /* file.sql.39 change */ INTEGER,
1279 fullName VARCHAR2,
1280 formatted INTEGER DEFAULT 1,
1281 allowChildDoc INTEGER DEFAULT 0,
1282 includePackage INTEGER DEFAULT 0) RETURN VARCHAR2
1283 IS
1284 docID jdr_paths.path_docid%TYPE;
1285 BEGIN
1286 --
1287 -- A non-null fullName indicates that this is the first time this function
1288 -- is being called for this document. If so, we need to find the
1289 -- document ID and start the export process.
1290 --
1291 IF (fullName IS NOT NULL) THEN
1292 mFormatted := formatted;
1293
1294 docID := getDocumentID(fullName, allowChildDoc = 1, includePackage = 1);
1295 IF (docID = -1) THEN
1296 -- Unable to find the document
1297 -- ###
1298 -- ### Give error if unable to locate document
1299 -- ###
1300 RETURN (NULL);
1301 END IF;
1302
1303 -- Determine if we're exporting a document or a package.
1304 --
1305 -- #(2417655) Save the path type in a package variable, so if/when we
1306 -- re-enter this procedure, we will know whether we are exporting
1307 -- a package or a document.
1308 SELECT path_type INTO mPathType FROM jdr_paths WHERE path_docid = docid;
1309 IF (mPathType = 'PACKAGE') THEN
1310 RETURN (exportPackageDocument(docID, mFormatted, exportFinished));
1311 ELSE
1312 RETURN (exportSingleDocument(docID, mFormatted, exportFinished));
1313 END IF;
1314 ELSE
1315 IF (mPathType = 'PACKAGE') THEN
1316 RETURN (exportPackageDocument(null, mFormatted, exportFinished));
1317 ELSE
1318 RETURN (exportSingleDocument(null, mFormatted, exportFinished));
1319 END IF;
1320 END IF;
1321 END;
1322
1323
1324 --
1325 -- Export the translations in XLIFF format. The document will be
1326 -- exported in 32k chunks.
1327 --
1328 FUNCTION exportXLIFFDocument(
1329 exportFinished OUT NOCOPY /* file.sql.39 change */ INTEGER,
1330 document VARCHAR2,
1331 language VARCHAR2) RETURN VARCHAR2
1332 IS
1333 r_trans c_trans%ROWTYPE;
1334 docID jdr_paths.path_docid%TYPE;
1335 chunk VARCHAR2(32000);
1336 newxml VARCHAR2(32000);
1337 baseLanguage jdr_attributes.att_value%TYPE;
1338 compref jdr_attributes_trans.atl_comp_ref%TYPE;
1339 source jdr_attributes.att_value%TYPE;
1340 dotpos INTEGER;
1341 BEGIN
1342 -- Assume that the document will fit in this 32k chunk
1343 exportFinished := 1;
1344
1345 IF (document IS NOT NULL) THEN
1346 docID := getDocumentID(document, TRUE, FALSE);
1347 IF (docID = -1) THEN
1348 RETURN (NULL);
1349 END IF;
1350
1351 -- Retrieve the base language. This is the same query which is
1352 -- executed in DBAccess.getBaseDevelopmentLanguage
1353 BEGIN
1354 SELECT att_value INTO baseLanguage FROM jdr_attributes
1355 WHERE att_comp_docid = docID AND
1356 att_name = 'xml:lang' AND
1357 att_comp_seq = 0;
1358 EXCEPTION
1359 WHEN NO_DATA_FOUND THEN
1360 baseLanguage := 'Unknown';
1361 END;
1362
1363 -- Create the XLIFF document
1364 chunk := '<?xml version = ''1.0'' encoding = ''UTF-8''?>' || NEWLINE ||
1365 '<!--DBDRV: -->' || NEWLINE ||
1366 '<xliff version="1.0">' || NEWLINE ||
1367 lpad(' ', INDENT_SIZE) || '<file datatype="jdr" original="' ||
1368 substr(document, instr(document, '/', -1) + 1) ||
1369 '" source-language="' || baseLanguage ||
1370 '" target-language="' || language || '">' || NEWLINE ||
1374 ELSE
1371 lpad(' ', INDENT_SIZE*2) || '<body>' || NEWLINE;
1372
1373 OPEN c_trans(docID, language);
1375 chunk := mPartialXLIFFChunk;
1376 END IF;
1377
1378 LOOP
1379 -- Get each tranlsation
1380 FETCH c_trans INTO r_trans;
1381 IF (c_trans%NOTFOUND) THEN
1382 CLOSE c_trans;
1383 EXIT;
1384 END IF;
1385
1386 -- component ref which equals "." indicates a top-level component
1387 IF (r_trans.atl_comp_ref = '.') THEN
1388 compref := jdr_mds_internal.getDocumentName(r_trans.atl_comp_docid);
1389 ELSIF (INSTR(r_trans.atl_comp_ref, ':') <> 1) THEN
1390 compref := jdr_mds_internal.getDocumentName(r_trans.atl_comp_docid) ||
1391 '..' || r_trans.atl_comp_ref;
1392 ELSE
1393 -- #(3260414) Views need to be handled specially. If the component
1394 -- reference is something like: :reg.region2, then the reference
1395 -- should be: docname:reg..region2...id, not docname..:reg.region2...id
1396 dotpos := INSTR(r_trans.atl_comp_ref, '.');
1397 compref := jdr_mds_internal.getDocumentName(r_trans.atl_comp_docid);
1398 IF (dotpos > 0) THEN
1399 compref := compref ||
1400 SUBSTR(r_trans.atl_comp_ref, 1, dotpos - 1) || '..' ||
1401 SUBSTR(r_trans.atl_comp_ref, dotpos + 1);
1402 ELSE
1403 compref := compref || r_trans.atl_comp_ref;
1404 END IF;
1405 END IF;
1406
1407 -- convert the /'s to .'s for XLIFF format
1408 compref := translate(compref, '/', '.');
1409
1410 -- #(3477218) We need to be able to get the source for customization
1411 -- views. Since this is more complicated non customization views, we
1412 -- are going to special case customization views.
1413 DECLARE
1414 viewID VARCHAR2(255);
1415 viewCompRef VARCHAR2(255);
1416 startSeq jdr_components.comp_seq%TYPE;
1417 endSeq jdr_components.comp_seq%TYPE;
1418 startLevel jdr_components.comp_level%TYPE;
1419 BEGIN
1420 IF (INSTR(r_trans.atl_comp_ref, ':') <> 1) THEN
1421 -- This is not a translation for a customization view.
1422 --
1423 -- #(3258371) Get the source for customization documents as well.
1424 SELECT
1425 source.att_value INTO source
1426 FROM
1427 jdr_components, jdr_attributes source, jdr_attributes custs
1428 WHERE
1429 source.att_comp_docid = r_trans.atl_comp_docid AND
1430 comp_docid = r_trans.atl_comp_docid AND
1431 custs.att_comp_docid = r_trans.atl_comp_docid AND
1432 source.att_name = r_trans.atl_name AND
1433 comp_seq = source.att_comp_seq AND
1434 comp_seq = custs.att_comp_seq AND
1435 (
1436 (
1437 custs.att_name = r_trans.atl_name AND
1438 (
1439 comp_id = r_trans.atl_comp_ref OR
1440 (comp_seq=0 AND r_trans.atl_comp_ref='.')
1441 )
1442 )
1443 OR
1444 (
1445 custs.att_name IN ('element') AND
1446 custs.att_value = r_trans.atl_comp_ref AND
1450 ELSE
1447 comp_element IN ('view', 'modify', 'move', 'insert')
1448 )
1449 );
1451 -- This is a translation for a customization view. As such,
1452 -- we need to restrict the query to components contained within
1453 -- the customization view.
1454 IF (dotpos = 0) THEN
1455 viewID := SUBSTR(r_trans.atl_comp_ref, 2);
1456 viewCompRef := NULL;
1457 ELSE
1458 viewID := SUBSTR(r_trans.atl_comp_ref, 2, dotpos - 2);
1459 viewCompRef := SUBSTR(r_trans.atl_comp_ref, dotpos + 1);
1460 END IF;
1461
1462 -- Get the starting sequence of the customization view
1463 SELECT comp_seq, comp_level INTO startSeq, startLevel
1464 FROM jdr_components
1465 WHERE comp_docid = r_trans.atl_comp_docid AND
1466 comp_element = 'view' AND
1467 comp_id = viewID;
1468
1469 -- and the ending sequence
1470 SELECT MAX(comp_seq) - 1 INTO endSeq
1471 FROM jdr_components
1472 WHERE comp_docid = r_trans.atl_comp_docid AND
1473 comp_seq > startSeq AND
1474 comp_level <= startLevel;
1475
1476 SELECT
1477 DISTINCT source.att_value INTO source
1478 FROM
1479 jdr_components, jdr_attributes source, jdr_attributes custs
1480 WHERE
1481 source.att_comp_docid = r_trans.atl_comp_docid AND
1482 comp_docid = r_trans.atl_comp_docid AND
1483 custs.att_comp_docid = r_trans.atl_comp_docid AND
1484 source.att_name = r_trans.atl_name AND
1485 comp_seq = source.att_comp_seq AND
1486 comp_seq = custs.att_comp_seq AND
1487 comp_seq >= startSeq AND
1488 (comp_seq <= endSeq OR endSeq IS NULL) AND
1489 comp_element IN ('view', 'modify', 'move', 'insert') AND
1490 --
1491 -- Either the viewCompRef is not null, indicating that the reference
1492 -- is not a top level component, in which case there must be
1493 -- an element attribute matching the component reference; or the
1494 -- viewCompRef is null, indicating the reference is a top level
1495 -- component, in which case there must not be an element attribute.
1496 --
1497 (
1498 (
1499 viewCompRef IS NOT NULL AND
1500 custs.att_name IN ('element') AND
1501 custs.att_value = viewCompRef
1502 ) OR
1503 (
1504 viewCompRef IS NULL AND
1505 comp_element IN ('view', 'modify') AND
1506 NOT EXISTS (SELECT att_name FROM jdr_attributes
1507 WHERE att_comp_docid = r_trans.atl_comp_docid AND
1508 att_comp_seq = comp_seq AND
1509 att_name = 'element')
1510 )
1511 );
1512 END IF;
1513 EXCEPTION
1514 -- Set source to NULL it this is a dangling translation
1515 WHEN no_data_found THEN
1516 source := NULL;
1517 END;
1518
1519 -- add the translation
1520 newxml := lpad(' ', INDENT_SIZE*3) ||
1521 '<trans-unit id="' ||
1522 compref || '...' || r_trans.atl_name || '" ' ||
1523 'translate="yes">' || NEWLINE ||
1524 lpad(' ', INDENT_SIZE*4) || '<source>' ||
1525 source || '</source>' || NEWLINE ||
1526 lpad(' ', INDENT_SIZE*4) || '<target>' ||
1527 r_trans.atl_value || '</target>' || NEWLINE ||
1528 lpad(' ', INDENT_SIZE*3) || '</trans-unit>' || NEWLINE;
1529
1530 addXMLtoChunk(chunk, newxml);
1531 END LOOP;
1532
1533 -- We are done, just need to add the closing tags
1534 newxml := lpad(' ', INDENT_SIZE*2) || '</body>' || NEWLINE ||
1535 lpad(' ', INDENT_SIZE) || '</file>' || NEWLINE ||
1536 '</xliff>' || NEWLINE;
1537 addXMLtoChunk(chunk, newxml);
1538
1539 mPartialXLIFFChunk := NULL;
1540 RETURN (chunk);
1541
1542 EXCEPTION
1543 WHEN chunk_size_exceeded THEN
1544 exportFinished := 0;
1545 mPartialXLIFFChunk := newxml;
1546 RETURN (chunk);
1547 END;
1548
1549
1550 --
1551 -- Retrieves the document id for the specified fully qualified path name.
1552 -- The pathname must begin with a '/' and should look something like:
1553 -- /oracle/apps/AK/mydocument
1554 --
1555 -- Parameters:
1556 -- fullPathName - the fully qualified name of the document
1557 -- pathType - the type of the document, either 'DOCUMENT' or 'PACKAGE'
1558 -- if no type is specified, and there happens to be a path
1559 -- of both 'DOCUMENT' and 'PACKAGE' (which is unlikely),
1560 -- then the id of the DOCUMENT will be returned
1561 --
1562 -- Returns:
1566 fullPathName VARCHAR2,
1563 -- Returns the ID of the path or -1 if no such path exists
1564 --
1565 FUNCTION getDocumentID(
1567 pathType VARCHAR2 DEFAULT NULL) RETURN NUMBER
1568 IS
1569 fullPath VARCHAR2(512);
1570 cacheName VARCHAR2(64);
1571 pathLevel INTEGER;
1572 endIdx INTEGER := -1;
1573 pathNames pathnametab := pathnametab();
1574 docID JDR_PATHS.PATH_DOCID%TYPE := -1;
1575 ownerID JDR_PATHS.PATH_OWNER_DOCID%TYPE := 0;
1576 pType JDR_PATHS.PATH_TYPE%TYPE;
1577 cachePackage BOOLEAN := TRUE;
1578 BEGIN
1579 -- #(3234805) If the document does not start with a forward slash,
1580 -- then it's an invalid document name
1581 IF (INSTR(fullPathName, '/') <> 1) THEN
1582 RETURN (-1);
1583 END IF;
1584
1585 -- Check if this is the root package
1586 IF ((fullPathName = '/') AND ((pathType IS NULL) OR (pathType = 'PACKAGE'))) THEN
1587 RETURN (0);
1588 END IF;
1589
1590 -- #(3403125) Remove the trailing and first slash
1591 fullPath := substr(fullPathName, instr(fullPathName, '/') + 1);
1592 IF (INSTR(fullPath, '/', LENGTH(fullPath)) > 0) THEN
1593 fullPath := SUBSTR(fullPath, 1, LENGTH(fullPath) - 1);
1594 END IF;
1595
1596 -- Break up the document name into the individual packages
1597 WHILE (endIdx <> 0) LOOP
1598 endIdx := INSTR(fullPath, '/');
1599 pathNames.extend;
1600 IF endIdx = 0 THEN
1601 -- This is the leaf path name
1602 pathNames(pathNames.COUNT) := fullPath;
1603 ELSE
1604 -- Get the next package and remove it from the full path name
1605 pathNames(pathNames.COUNT) := substr(fullPath, 1, endIdx - 1);
1606 fullPath := substr(fullPath, endIdx + 1);
1607 END IF;
1608 END LOOP;
1609
1610 -- #(3803543) Check if there is a name in the cache. We do this to
1611 -- reduce the amount of SQL. This is specific to apps and the only
1612 -- packages which are cached are:
1613 -- /oracle/apps/xxx and
1614 -- /oracle/apps/xxx/customizations
1615 -- For /oracle/apps/xxx, the key is 'xxx'.
1616 -- For /oracle/apps/xxx/customizations, the key is '/xxx'.
1617 -- bug #(4137848) Lookup cache only if path atleast 3 levels
1618 -- to Avoid Subscript beyond count error
1619 IF (pathNames.COUNT > 2) THEN
1620 -- If the path does not begin with /oracle/apps, then no need to check
1621 -- the cache.
1622 IF (pathNames(1) = 'oracle' and pathNames(2) = 'apps') THEN
1623 IF (pathNames.COUNT > 3) AND (pathNames(4) = 'customizations') THEN
1624 -- If this path is in the cache, then we no we will be able to skip
1625 -- the /oracle/apps/xxx/customizations. As such, we set the path
1626 -- level to 5, which is the level after customizations.
1627 pathLevel := 5;
1628 cacheName := '/'||pathNames(3);
1629 ELSE
1630 -- If this path is in the cache, then we no we will be able to skip
1631 -- the /oracle/apps/xxx. As such, we set the path level to 4, which
1632 -- is the level after 'xxx'.
1633 pathLevel := 4;
1634 cacheName := pathNames(3);
1635 END IF;
1636
1637 -- Now that we know the key, search the cache
1638 docID := mPackageCache.FIRST;
1639 WHILE docID IS NOT NULL LOOP
1640 IF (cacheName = mPackageCache(docID)) THEN
1641 cachePackage := FALSE;
1642 ownerID := docID;
1643 docID := NULL;
1644 ELSE
1645 docID := mPackageCache.NEXT(docID);
1646 END IF;
1647 END LOOP;
1648 END IF;
1649 END IF;
1650
1651 -- ownerID will be non-zero if part of the document is in the cache
1652 IF (ownerID = 0) THEN
1653 -- Since the document was not in the cache, we have to go through each
1654 -- of the packages
1655 pathLevel := 1;
1656 ELSIF (pathLevel > pathNames.COUNT) THEN
1657 RETURN (ownerID);
1658 END IF;
1659
1660 -- Loop through the remaining packages, getting the child package, until
1661 -- we are at the leaf package
1662 LOOP
1663 SELECT path_docid, path_type
1664 INTO docID, pType
1665 FROM jdr_paths
1666 WHERE path_name = pathNames(pathLevel) AND path_owner_docid = ownerID;
1667
1668 -- Check to see if the package should be cached
1669 IF (cachePackage) THEN
1670 IF (pathLevel = 1) THEN
1671 -- We only cache names beginning with /oracle/apps, so if the first
1672 -- name is not 'oracle', we should not cache the name
1673 IF (pathNames(1) <> 'oracle') THEN
1674 cachePackage := FALSE;
1675 END IF;
1676 ELSIF (pathLevel = 2) THEN
1677 -- We only cache names beginning with /oracle/apps, so if the second
1678 -- name is not 'apps', we should not cache the name
1679 IF (pathNames(2) <> 'apps') THEN
1680 cachePackage := FALSE;
1681 END IF;
1682 ELSIF (pathLevel = 3) THEN
1683 -- Since the first two packages are /oracle/apps and since this is
1684 -- the third package, we should cache this value.
1685 mPackageCache(docID) := pathNames(3);
1686 ELSIF (pathLevel = 4) THEN
1690 IF (pathNames(4) = 'customizations') THEN
1687 -- We only cache names beginning with /oracle/apps/xxx/customizations,
1688 -- so if the 4th name is not 'customizations', we should not cache
1689 -- the name
1691 -- This is a package of the form, /oracle/apps/xxx/customizations,
1692 -- so cache this as '/xxx'
1693 mPackageCache(docID) := '/'||pathNames(3);
1694 END IF;
1695 cachePackage := FALSE;
1696 ELSE
1697 -- We do not cache any packages more than 5 levels deep
1698 cachePackage := FALSE;
1699 END IF;
1700 END IF;
1701
1702 -- Check if this is the leaf package
1703 IF (pathLevel = pathNames.COUNT) THEN
1704 IF (pathType IS NULL) OR (pathType = pType) THEN
1705 RETURN (docID);
1706 ELSE
1707 RETURN (-1);
1708 END IF;
1709 END IF;
1710
1711 -- Get ready for the next package
1712 ownerID := docID;
1713 pathLevel := pathLevel + 1;
1714 END LOOP;
1715 EXCEPTION
1716 WHEN NO_DATA_FOUND THEN
1717 RETURN (-1);
1718 END;
1719
1720
1721 --
1722 -- Retrieves the document id for the specified attributes. This is
1723 -- typically used when attempting to find the id of a path which is
1724 -- owned by a package document.
1725 --
1726 -- Note that this will return the docID which matches the specified
1727 -- name, ownerID and docType (not pathSeq). For the pathSeq, it will
1728 -- update the database path_seq to the value specified in pathSeq.
1729 --
1730 -- Parameters:
1731 -- name - the name of the document (not fully qualified)
1732 -- ownerID - the ID of the owning package
1733 -- pathSeq - the path sequence
1734 -- docType - either 'DOCUMENT' or 'PACKAGE'
1735 --
1736 -- Returns:
1737 -- Returns the ID of the path or -1 if no such path exists
1738 --
1739 FUNCTION getDocumentID(
1740 name VARCHAR2,
1741 ownerID JDR_PATHS.PATH_OWNER_DOCID%TYPE,
1742 pathSeq JDR_PATHS.PATH_SEQ%TYPE,
1743 docType JDR_PATHS.PATH_TYPE%TYPE) RETURN NUMBER
1744 IS
1745 docid JDR_PATHS.PATH_OWNER_DOCID%TYPE;
1746 seq JDR_PATHS.PATH_SEQ%TYPE;
1747 BEGIN
1748 -- Find the docid for the specified attributes
1749 SELECT path_docid, path_seq INTO docid, seq
1750 FROM jdr_paths
1751 WHERE
1752 path_name = name AND
1753 path_owner_docid = ownerID AND
1754 path_type = docType;
1755
1756 -- If the sequence differs, then update it to the specified sequence
1757 IF (seq <> pathSeq) THEN
1758 UPDATE jdr_paths SET path_seq = pathSeq
1759 WHERE
1760 path_docid = docid;
1761 END IF;
1762
1763 RETURN (docid);
1764
1765 EXCEPTION
1766 WHEN NO_DATA_FOUND THEN
1767 RETURN (-1);
1768 END;
1769
1770
1771 --
1772 -- For each document name, retrieve the corresponding document ID.
1773 -- The document ID for docs[i] is in docIDs[i]. If no documentID
1774 -- exists for a docs[i], then docIDs[i] = -1.
1775 --
1776 PROCEDURE getDocumentIDs(docs IN jdr_stringArray,
1777 docIDs OUT NOCOPY /* file.sql.39 change */ jdr_numArray)
1778 IS
1779 i NUMBER;
1780 ids jdr_numArray := jdr_numArray(null);
1781 BEGIN
1782 FOR i IN 1..docs.COUNT LOOP
1783 IF (i <> 1)
1784 THEN
1785 ids.extend;
1786 END IF;
1787 ids(ids.COUNT) := getDocumentID(docs(i), 'DOCUMENT');
1788 END LOOP;
1789 docIDs := ids;
1790 END;
1791
1792
1793 --
1794 -- Given the document id, find the fully qualified document name
1795 --
1796 -- Parameters:
1797 -- docID - the ID of the document
1798 --
1799 -- Returns:
1800 -- the fully qualified document name
1801 --
1802 FUNCTION getDocumentName(
1803 docid NUMBER) RETURN VARCHAR2
1804 IS
1805 pathNames pathnametab;
1806 name VARCHAR2(512) := '';
1807 i INTEGER;
1808 BEGIN
1809 SELECT path_name BULK COLLECT INTO pathNames
1810 FROM jdr_paths
1811 START WITH path_docid = docid
1812 CONNECT BY PRIOR path_owner_docid = path_docid
1813 ORDER BY LEVEL DESC;
1814
1815 FOR i IN 1..pathNames.COUNT LOOP
1816 name := name || '/' || pathNames(i);
1817 END LOOP;
1818
1819 RETURN (name);
1820 END;
1821
1822
1823 --
1824 -- Given the document id of a child document, find the id for the
1825 -- owning package document.
1826 --
1827 -- Parameters:
1828 -- docID - the ID of the child document
1829 --
1830 -- Returns:
1831 -- Returns the ID of the package document or -1 if not found
1832 --
1833 FUNCTION getPackageDocument(
1834 docID NUMBER) RETURN NUMBER
1835 IS
1836 pathSeq jdr_paths.path_seq%TYPE := 0;
1837 ownerID jdr_paths.path_owner_docid%TYPE;
1838 newDocid jdr_paths.path_docid%TYPE := docID;
1839 BEGIN
1840 LOOP
1841 -- Retrieve the parent of the previous document
1842 SELECT path_owner_docid, path_seq
1846 EXIT WHEN pathSeq <= 0;
1843 INTO ownerID, pathSeq
1844 FROM jdr_paths
1845 WHERE path_docid = newDocID;
1847 newDocID := ownerID;
1848 END LOOP;
1849
1850 -- Makse sure that we have retrieved a package document
1851 IF (pathSeq = 0) THEN
1852 RETURN (newDocID);
1853 ELSE
1854 RETURN (-1);
1855 END IF;
1856 EXCEPTION
1857 WHEN NO_DATA_FOUND THEN
1858 RETURN (-1);
1859 END;
1860
1861
1862 --
1863 -- Gets the minimun version of JRAD with which the repository API is
1864 -- compatible. That is, the actual JRAD version must be >= to the
1865 -- minimum version of JRAD in order for the repositroy API and java code to
1866 -- be compatible.
1867 --
1868 -- Returns:
1869 -- Returns the mimumum version of JRAD
1870 --
1871 FUNCTION getMinJRADVersion RETURN VARCHAR2
1872 IS
1873 BEGIN
1874 RETURN (MIN_JRAD_VERSION);
1875 END;
1876
1877
1878
1879 --
1880 -- Gets the version of the repository API. This API version must >= to the
1881 -- java constant CompatibleVersions.MIN_REPOS_VERSION.
1882 --
1883 -- Returns:
1884 -- Returns the version of the repository
1885 --
1886 FUNCTION getRepositoryVersion RETURN VARCHAR2
1887 IS
1888 BEGIN
1889 RETURN (REPOS_VERSION);
1890 END;
1891
1892 --
1893 -- Lock the document, given a valid docID. Before updating/saving a document,
1894 -- it needs to be locked to ensure that it is not updated simultaneously by
1895 -- multipleusers. If the document is already locked, we will continue to
1896 -- attempt to lock the document for a user-specified number of seconds, or
1897 -- MAX_SECONDS_TO_WAIT_FOR_LOCK by default, before giving up.
1898 -- If, after that time, we have still not locked the document, a
1899 -- "RESOURCE_BUSY" exception will be raised.
1900 --
1901 -- Parameters:
1902 -- docID - ID of the document to lock
1903 -- attempts - number of seconds to wait for a lock
1904 --
1905 PROCEDURE lockDocument(
1906 docID JDR_PATHS.PATH_DOCID%TYPE,
1907 attempts INTEGER DEFAULT MAX_SECONDS_TO_WAIT_FOR_LOCK
1908 )
1909 IS
1910 tmpdocID JDR_PATHS.PATH_DOCID%TYPE;
1911 cont BOOLEAN := TRUE;
1912 num_attempts INTEGER := attempts;
1913 RESOURCE_BUSY EXCEPTION;
1914 PRAGMA EXCEPTION_INIT(RESOURCE_BUSY, -54);
1915 BEGIN
1916 WHILE (cont) LOOP
1917 BEGIN
1918 SELECT path_docid INTO tmpdocID
1919 FROM jdr_paths
1920 WHERE path_docid = docID
1921 FOR UPDATE NOWAIT;
1922
1923 cont := false;
1924 EXCEPTION
1925 WHEN RESOURCE_BUSY THEN
1926 num_attempts := num_attempts - 1;
1927 IF (num_attempts <= 0) THEN
1928 RAISE;
1929 END IF;
1930 SYS.DBMS_LOCK.SLEEP(1);
1931 END;
1932 END LOOP;
1933 END;
1934
1935
1936 --
1937 -- Performs all steps that are necessary before a top-levle document is
1938 -- saved/updated which includes:
1939 -- (1) If the document already exists, updates the "who" columns in
1940 -- JDR_PATHS and deletes the contents of the document
1941 -- (2) If the document does not exist yet, creates a new entry in the
1942 -- JDR_PATHS table
1943 --
1944 -- Parameters:
1945 -- username - user who is updating/inserting the document
1946 -- fullPathName - fully qualified name of the document/package file
1947 -- pathType - 'DOCUMENT' for single document or 'PACKAGE' for package
1948 -- file
1949 -- xmlversion - xml version
1950 -- xmlencoding - xml encoding
1951
1952 --
1953 -- Returns:
1954 -- Returns the ID of the document or -1 if an error occurred
1955 --
1956 FUNCTION prepareDocumentForInsert(
1957 username VARCHAR2,
1958 fullPathName VARCHAR2,
1959 pathType VARCHAR2,
1960 xmlversion VARCHAR2,
1961 xmlencoding VARCHAR2) RETURN NUMBER
1962 IS
1963 docID jdr_paths.path_docid%TYPE;
1964 pathSeq jdr_paths.path_seq%TYPE;
1965 BEGIN
1966
1967 -- Check if the document already exists
1968 docID := getDocumentID(fullPathName, pathType);
1969
1970 IF (docID = -1) THEN
1971 BEGIN
1972 -- Document does not exist yet, so create it now
1973 docID := createPath(username,
1974 fullPathName,
1975 pathType,
1976 xmlversion,
1977 xmlencoding);
1978 EXCEPTION
1979 -- #(2669626) If the sequence, JDR_DOCUMENT_ID_S, is corrupt (which can
1980 -- happen if it gets reset), then createPath will raise a NO_DATA_FOUND
1981 -- exception.
1982 WHEN corrupt_sequence THEN
1983 raise_application_error(ERROR_CORRUPT_SEQUENCE, NULL);
1984
1985 -- #(2456503) If we were unable to create the path, it is likely due
1986 -- to an already existing document or package
1987 WHEN document_name_conflict THEN
1988 IF (pathType = 'DOCUMENT') THEN
1992 END IF;
1989 raise_application_error(ERROR_DOCUMENT_NAME_CONFLICT, NULL);
1990 ELSE
1991 raise_application_error(ERROR_PACKAGE_NAME_CONFLICT, NULL);
1993 END;
1994 ELSE
1995 -- #(2456503) Make sure that the document/package file does not already
1996 -- conflict with a document/package which already exists.
1997 SELECT path_seq INTO pathSeq FROM jdr_paths WHERE path_docid = docID;
1998
1999 -- If attempting to save a document, then the path which we retrieved
2000 -- must be a top-level document (i.e. pathSeq = -1). If pathSeq > 0,
2001 -- this means that there is already a document of the same name inside a
2002 -- package file; and if pathSeq = 0, this means that a package file of
2003 -- the same name already exists in the repository.
2004 IF ((pathType = 'DOCUMENT') AND (pathSeq <> -1)) THEN
2005 raise_application_error(ERROR_DOCUMENT_NAME_CONFLICT, NULL);
2006 END IF;
2007
2008 -- If attempting to save a package file, then the path which we retrieved
2009 -- must be a package file. If pathSeq = -1, then this means there is
2010 -- already a package (not a package file) of the same name; and
2011 -- if pathSeq > 0, this means there is already a package in a package file
2012 -- of the same name.
2013 IF ((pathType = 'PACKAGE') AND (pathSeq <> 0)) THEN
2014 raise_application_error(ERROR_PACKAGE_NAME_CONFLICT, NULL);
2015 END IF;
2016
2017 -- Lock the document
2018 lockDocument(docID);
2019
2020 -- Document already exists, so update the "who" columns
2021 UPDATE jdr_paths
2022 SET path_xml_version = xmlversion,
2023 path_xml_encoding = xmlencoding,
2024 last_updated_by = username,
2025 last_update_date = SYSDATE,
2026 last_update_login = username
2027 WHERE path_docid = docID;
2028
2029 -- And delete the "old" contents of the document
2030 deleteDocument(docID, FALSE);
2031 END IF;
2032
2033 RETURN (docID);
2034 END;
2035
2036
2037 --
2038 -- Performs all steps that are necessary before a package document is
2039 -- saved/updated which includes:
2040 -- (1) If the document already exists, updates the "who" columns in
2041 -- JDR_PATHS
2042 -- (2) If the document does not exist yet, creates a new entry in the
2043 -- JDR_PATHS table
2044 --
2045 -- Parameters:
2046 -- username - user who is updating/inserting the document
2047 -- pathname - name of the document/package
2048 -- ownerID - ID of the owning package
2049 -- pathSeq - path sequence
2050 -- pathType - 'DOCUMENT' or 'PACKAGE'
2051 --
2052 -- Returns:
2053 -- Returns the ID of the document or -1 if an error occurred
2054 --
2055 FUNCTION prepareDocumentForInsert(
2056 username VARCHAR2,
2057 pathname VARCHAR2,
2058 ownerID JDR_PATHS.PATH_OWNER_DOCID%TYPE,
2059 pathSeq JDR_PATHS.PATH_SEQ%TYPE,
2060 pathType JDR_PATHS.PATH_TYPE%TYPE) RETURN NUMBER
2061 IS
2062 docID jdr_paths.path_docid%TYPE;
2063 BEGIN
2064
2065 -- Check if the document already exists
2066 docID := getDocumentID(pathname, ownerID, pathSeq, pathType);
2067
2068 IF (docID = -1) THEN
2069 -- Document does not exist yet, so create it now
2070 docID := createPath(username, pathname, ownerID, pathSeq, pathType);
2071 ELSE
2072 -- Document already exists, so update the "who" columns
2073 UPDATE jdr_paths
2074 SET last_updated_by = username,
2075 last_update_date = SYSDATE,
2076 last_update_login = username
2077 WHERE path_docid = docID;
2078 END IF;
2079
2080 RETURN (docID);
2081 EXCEPTION
2082 WHEN corrupt_sequence THEN
2083 raise_application_error(ERROR_CORRUPT_SEQUENCE, NULL);
2084
2085 WHEN document_name_conflict THEN
2086 raise_application_error(ERROR_PACKAGE_NAME_CONFLICT, NULL);
2087 END;
2088
2089
2090 FUNCTION refactor(
2091 p_oldName VARCHAR2,
2092 p_newName VARCHAR2,
2093 p_translations INTEGER DEFAULT 1) RETURN INTEGER
2094 IS
2095 -- For each customized document which customizes the specified base
2096 -- document, retrieves the attributes for the specified component ID.
2097 CURSOR c_attributes(fullName VARCHAR2, baseName VARCHAR2, compID VARCHAR2) IS
2098 SELECT att_comp_docid,
2099 att_comp_seq,
2100 att_seq,
2101 att_name
2102 FROM (SELECT path_docid
2103 FROM jdr_attributes, jdr_paths
2104 WHERE path_docid = att_comp_docid AND
2105 path_name = baseName AND
2106 att_comp_seq = 0 AND
2107 att_name = 'customizes' AND
2108 att_value = fullName) docids,
2109 jdr_attributes, jdr_components
2110 WHERE att_comp_docid = docids.path_docid AND
2111 att_comp_docid = comp_docid AND
2112 att_comp_seq = comp_seq AND
2113 att_name IN ('element', 'before', 'after', 'parent') AND
2114 att_value = compID AND
2115 comp_element IN ('view', 'modify', 'move', 'insert', 'criterion');
2116
2120 SELECT atl_comp_docid,
2117 -- For each customized document which customizes the specified base
2118 -- document, retrieves the translations for the specified component ID.
2119 CURSOR c_translations(fullName VARCHAR2, baseName VARCHAR2, compID VARCHAR2) IS
2121 atl_comp_ref
2122 FROM (SELECT path_docid
2123 FROM jdr_attributes, jdr_paths
2124 WHERE path_docid = att_comp_docid AND
2125 path_name = baseName AND
2126 att_comp_seq = 0 AND
2127 att_name = 'customizes' AND
2128 att_value = fullName) docids,
2129 jdr_attributes_trans
2130 WHERE atl_comp_docid = docids.path_docid AND
2131 (atl_comp_ref = compID OR atl_comp_ref like ':%.'||compID);
2132
2133 -- Retrieve all of the customization documents which customize the
2134 -- specified base document.
2135 CURSOR c_documents(fullName VARCHAR2, baseName VARCHAR2) IS
2136 SELECT path_docid, created_by, jdr_mds_internal.getDocumentName(path_docid)
2137 FROM jdr_attributes, jdr_paths
2138 WHERE path_docid = att_comp_docid AND
2139 path_name = baseName AND
2140 att_comp_seq = 0 AND
2141 att_name = 'customizes' AND
2142 att_value = fullName;
2143
2144 oldDocName VARCHAR2(512);
2145 newDocName VARCHAR2(512);
2146 oldCustDocName VARCHAR2(512);
2147 newCustDocName VARCHAR2(512);
2148 oldBaseName jdr_paths.path_name%TYPE;
2149 newBaseName jdr_paths.path_name%TYPE;
2150 oldCompName jdr_components.comp_id%TYPE;
2151 newCompName jdr_components.comp_id%TYPE;
2152 docID jdr_paths.path_docid%TYPE;
2153 ownerID jdr_paths.path_docid%TYPE;
2154 attCompSeq jdr_attributes.att_comp_seq%TYPE;
2155 attSeq jdr_attributes.att_seq%TYPE;
2156 attName jdr_attributes.att_name%TYPE;
2157 oldCompRef jdr_attributes_trans.atl_comp_ref%TYPE;
2158 newCompRef jdr_attributes_trans.atl_comp_ref%TYPE;
2159 username jdr_paths.created_by%TYPE;
2160 oldPeriodPos INTEGER;
2161 newPeriodPos INTEGER;
2162 slashPos INTEGER;
2163 custPos INTEGER;
2164 pos1 INTEGER;
2165 pos2 INTEGER;
2166 changesMade INTEGER := 0;
2167 BEGIN
2168 -- Perform some simple check on the document name
2169 IF ( (INSTR(p_oldName, '/') <> 1) OR (INSTR(p_newName, '/') <> 1) ) THEN
2170 raise_application_error(ERROR_INVALID_NAME, NULL);
2171 END IF;
2172
2173 -- If the names contain a ".", it means that we are refactoring
2174 -- a component. Otherwise, we are refactoring a document
2175 oldPeriodPos := INSTR(p_oldName, '.');
2176 newPeriodPos := INSTR(p_newName, '.');
2177
2178 -- Make sure we are not trying to map a component to a document or
2179 -- vice versa
2180 IF ( ((oldPeriodPos = 0) AND (newPeriodPos > 0)) OR
2181 ((oldPeriodPos > 0) AND (newPeriodPos = 0)) ) THEN
2182 raise_application_error(ERROR_INCONSISTENT_MAPPING, NULL);
2183 END IF;
2184
2185 IF (oldPeriodPos > 0) THEN
2186 -- Break up the old component name into the component ID, base name and
2187 -- document name
2188 slashPos := INSTR(p_oldName, '/', -1);
2189 oldCompName := SUBSTR(p_oldName, oldPeriodPos + 1);
2190 oldBaseName := SUBSTR(p_oldName, slashPos + 1, oldPeriodPos - slashPos - 1);
2191 oldDocName := SUBSTR(p_oldName, 1, oldPeriodPos - 1);
2192
2193 -- Break up the new compoent name into the component ID, base name and
2194 -- document name
2195 slashPos := INSTR(p_newName, '/', -1);
2196 newCompName := SUBSTR(p_newName, newPeriodPos + 1);
2197 newBaseName := SUBSTR(p_newName, slashPos + 1, newPeriodPos - slashPos - 1);
2198 newDocName := SUBSTR(p_newName, 1, newPeriodPos - 1);
2199
2200 -- We do not support both renaming a component and renaming a document
2201 -- with the same mapping, so throw an error if this is the case
2202 IF (oldDocName <> newDocName) THEN
2203 raise_application_error(ERROR_ILLEGAL_MAPPING, NULL);
2204 END IF;
2205
2206 -- Make any necessary changes to the customization documents,
2207 -- replacing the old component name eith the new component name
2208 OPEN c_attributes(oldDocName, oldBaseName, oldCompName);
2209 LOOP
2210 FETCH c_attributes INTO docID, attCompSeq, attSeq, attName;
2211 IF (c_attributes%NOTFOUND) THEN
2212 CLOSE c_attributes;
2213 EXIT;
2214 END IF;
2215
2216 UPDATE jdr_attributes SET att_value = newCompName
2217 WHERE att_comp_docid = docID AND
2218 att_comp_seq = attCompSeq AND
2219 att_seq = attSeq AND
2220 att_name = attName;
2221
2222 changesMade := changesMade + 1;
2223 END LOOP;
2224
2225 -- Make any changes to translations, replacing the old component name
2226 -- with the new component name.
2227 IF (p_translations <> 0) THEN
2228 OPEN c_translations(oldDocName, oldBaseName, oldCompName);
2229 LOOP
2230 FETCH c_translations INTO docID, oldCompRef;
2231 IF (c_translations%NOTFOUND) THEN
2232 CLOSE c_translations;
2233 EXIT;
2237 -- :theview.componentID
2234 END IF;
2235
2236 -- For customization views, the reference is of the form:
2238 IF (INSTR(oldCompRef, ':') = 1) THEN
2239 newCompRef := SUBSTR(oldCompRef, 1, INSTR(oldCompRef, '.')) || newCompName;
2240 ELSE
2241 newCompRef := newCompName;
2242 END IF;
2243
2244 UPDATE jdr_attributes_trans SET atl_comp_ref = newCompRef
2245 WHERE atl_comp_docid = docID AND
2246 atl_comp_ref = oldCompRef;
2247
2248 changesMade := changesMade + 1;
2249 END LOOP;
2250 END IF;
2251 ELSE
2252 -- Since no component ID was specified, it means that we are dealing
2253 -- with the refactoring of a document, as opposed to a component
2254
2255 -- Get the new base name and document name
2256 newDocName := p_newName;
2257 oldDocName := p_oldName;
2258 oldBaseName := SUBSTR(p_oldName, INSTR(p_oldName, '/', -1) + 1);
2259 newBaseName := SUBSTR(p_newName, INSTR(p_newName, '/', -1) + 1);
2260
2261 -- Get each of the customization documents
2262 OPEN c_documents(oldDocName, oldBaseName);
2263 LOOP
2264 FETCH c_documents INTO docID, username, oldCustDocName;
2265 IF (c_documents%NOTFOUND) THEN
2266 CLOSE c_documents;
2267 EXIT;
2268 END IF;
2269
2270 -- For each customization document, we need to rename the customization
2271 -- document and change the customizes attributes. To rename the
2272 -- customization document, we first need to determine the new name of
2273 -- the customization document.
2274 --
2275 -- #(3455760) Originally, we always used the new style naming for the
2276 -- customization documents. That is, we would convert old style
2277 -- customizations to the new naming style. However, for consistency
2278 -- reasons, it was decided that it makes more sense to convert old
2279 -- style to old style and new style to new style.
2280 --
2281 -- For new style customizations, suppose the customization
2282 -- document is:
2283 -- /oracle/apps/jrad/webui/customizations/site/tower/musicpage
2284 -- And the new base document is:
2285 -- /oracle/apps/newjrad/webui/musicpage
2286 -- Then the new customization document is:
2287 -- /oracle/apps/newjrad/webui/customizations/site/tower/musicpage
2288 -- which is combination of:
2289 -- (1) the path name of the new document +
2290 -- (2) "customizations" +
2291 -- (3) layer type and layer value of the customization document +
2292 -- (4) the base name of the new document
2293 --
2294 -- For old style customizations, suppose the customization document
2295 -- is:
2296 -- /oracle/apps/jrad/customizations/site/tower/webui/musicpage
2297 -- And the new base document is:
2298 -- /oracle/apps/newjrad/webui/musicpage
2299 -- Then the new customization document is:
2300 -- /oracle/apps/newjrad/customizations/site/tower/webui/musicpage
2301 -- which is a combination of:
2302 -- (1) The first three packages of the new document +
2303 -- (2) "customizations" +
2304 -- (3) layer type and layer value of the customization document +
2305 -- (4) the remaining portion of the new document (minus the first three)
2306 --
2307 -- To determine whether or not this is an old style document, we
2308 -- simply need to count the slashes after the customizes portion. It's
2309 -- an old style customizations if there are more than 3 slashes in the
2310 -- customizes portions, since new style customizations will have exactly
2311 -- 3 slashes (one for the layer type, one for the layer value and one
2312 -- for the document name).
2313 custPos := INSTR(oldCustDocName, '/customizations');
2314 IF (INSTR(oldCustDocName, '/', custPos + 1, 4) <> 0) THEN
2318 -- and that the new document name is:
2315 -- This is an old style customization. For the following, suppose
2316 -- that the old customization document is :
2317 -- /oracle/apps/jrad/customizations/site/tower/webui/musicpage
2319 -- /oracle/apps/newjrad/webui/musicpage
2320 --
2321 -- (1) Get the first 3 packages of the new name.
2322 -- /oracle/apps/newjrad/
2323 newCustDocName := SUBSTR(newDocName, 1, INSTR(newDocName, '/', 1, 4));
2324
2325 -- (2) Add the customizations portion
2326 -- /oracle/apps/newjrad/customizations
2327 newCustDocName := newCustDocName || 'customizations';
2328
2329 -- (3) Add the layer type and layer value
2330 -- /oracle/apps/newjrad/customizations/site/tower/
2331 pos1 := INSTR(oldCustDocName, '/', custpos + 1, 1);
2332 pos2 := INSTR(oldCustDocName, '/', custpos + 1, 3);
2333 newCustDocName := newCustDocName ||
2334 SUBSTR(oldCustDocName, pos1, pos2 - pos1 + 1);
2335
2336 -- (4) Add the remaining portion of the new document
2337 -- /oracle/apps/newjrad/customizations/site/tower/webui/musicpage
2338 newCustDocName := newCustDocName ||
2339 SUBSTR(newDocName, INSTR(newDocName, '/', 1, 4) + 1);
2340 ELSE
2341 -- This is an new style customization. For the following, suppose
2342 -- that the old customization document is :
2343 -- /oracle/apps/jrad/webui/customizations/site/tower/musicpage
2344 -- and that the new document name is:
2345 -- /oracle/apps/newjrad/webui/musicpage
2346 --
2347 -- (1) Get the first path name of the new document
2348 -- /oracle/apps/newjrad/webui/
2349 newCustDocName := SUBSTR(newDocName, 1, INSTR(newDocName, '/', -1));
2350
2351 -- (2) Add the customizations portion
2352 -- /oracle/apps/newjrad/webui/customizations
2353 newCustDocName := newCustDocName || 'customizations';
2354
2355 -- (3) Add the layer type and layer value
2356 -- /oracle/apps/newjrad/webui/customizations/site/tower/
2357 pos1 := INSTR(oldCustDocName, '/', custpos + 1, 1);
2358 pos2 := INSTR(oldCustDocName, '/', custpos + 1, 3);
2359 newCustDocName := newCustDocName ||
2360 SUBSTR(oldCustDocName, pos1, pos2 - pos1 + 1);
2361
2362 -- (4) Add base name of the new document
2363 -- /oracle/apps/newjrad/webui/customizations/site/tower/musicpage
2364 newCustDocName := newCustDocName || newBaseName;
2365 END IF;
2366
2367 -- Create the path for the new customized document. If the path
2368 -- already exists, it will simply return the owner.
2369 ownerID := createPath(username,
2370 RTRIM(newCustDocName, newBaseName), '', '', '');
2371 IF (ownerID = -1) THEN
2372 raise_application_error(ERROR_INVALID_NAME, NULL);
2373 END IF;
2374
2375 -- Update the path with the new base name and new owner ID
2376 BEGIN
2377 UPDATE jdr_paths
2378 SET path_name = newBaseName,
2379 path_owner_docid = ownerID
2380 WHERE path_docid = docID;
2381 EXCEPTION
2382 -- This can happen if the customization already exists on the
2383 -- refactored base document. If so, do not attempt to overwrite
2384 -- the existing customization.
2385 WHEN DUP_VAL_ON_INDEX THEN
2386 GOTO end_loop;
2387 END;
2388
2389 -- Make the necessary changes to the customization documents, replacing
2390 -- the customizes attribute with the new base document name.
2391 UPDATE jdr_attributes
2392 SET att_value = newDocName
2393 WHERE att_comp_docid = docID AND
2394 att_comp_seq = 0 AND
2395 att_name = 'customizes' AND
2396 att_value = oldDocName;
2397
2398 -- #(3456035) Modify the package attribute if the document has been
2399 -- moved to a new package
2400 IF (RTRIM(p_oldName, oldBaseName) <> RTRIM(p_newName, newBaseName)) THEN
2401 UPDATE jdr_attributes
2402 SET att_value = REPLACE(newCustDocName, '/'||newBaseName)
2403 WHERE att_comp_docid = docID AND
2404 att_comp_seq = 0 AND
2405 att_name = 'package';
2406 END IF;
2407
2408 changesMade := changesMade + 1;
2409
2410 <<end_loop>>
2411 NULL;
2412 END LOOP;
2413 END IF;
2414
2415 RETURN (changesMade);
2416 END;
2417
2418 --
2419 -- Remove any dangling references of the document. This is only necessary
2420 -- for package documents. Here's a scenario where this is relevant.
2421 -- Suppose you have a package document FOO which has child documents A, B and
2422 -- C. Now suppose you are updating FOO, this time removing document C.
2423 -- In order make sure that all references to C are destroyed, this needs to
2424 -- be called after saving FOO.
2425 --
2426 PROCEDURE removeDanglingReferences(
2427 docID JDR_PATHS.PATH_DOCID%TYPE)
2428 IS
2429 BEGIN
2430 DELETE jdr_paths
2431 WHERE path_seq=-2 AND path_docid IN
2432 (SELECT path_docid FROM jdr_paths
2433 START WITH path_docid = docid
2434 CONNECT BY PRIOR path_docid=path_owner_docid);
2435 END;
2436
2437
2438 BEGIN
2439 -- Initialize the stack for exporting XML
2440 mPackageStack := NumArray(NULL);
2441 mPackageStack.EXTEND(mPackageStack.LIMIT-1,1);
2442
2443 mPackageNames := CharArray(NULL);
2444 mPackageNames.EXTEND(mPackageNames.LIMIT-1,1);
2445
2449 END;
2446 mStack := CharArray(NULL);
2447 mStack.EXTEND(mStack.LIMIT-1,1);
2448