Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 1cdeb9c

Browse files
committed
Refactor annotation processing and enhance line scanning functionality in ut_utils
1 parent 40eb166 commit 1cdeb9c

2 files changed

Lines changed: 147 additions & 117 deletions

File tree

source/core/annotations/ut_annotation_manager.pkb

Lines changed: 32 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -143,31 +143,41 @@ create or replace package body ut_annotation_manager as
143143
l_parse_time date := sysdate;
144144
pragma autonomous_transaction;
145145
begin
146-
loop
147-
fetch a_sources_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit;
148-
for i in 1 .. l_names.count loop
149-
if l_names(i) != l_name then
150-
l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type);
151-
ut_annotation_cache_manager.update_cache(
152-
ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations)
153-
);
154-
l_object_lines.delete;
155-
end if;
146+
begin
147+
loop
148+
fetch a_sources_cursor bulk collect into l_names, l_lines limit c_lines_fetch_limit;
149+
for i in 1 .. l_names.count loop
150+
if l_names(i) != l_name then
151+
l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type);
152+
ut_annotation_cache_manager.update_cache(
153+
ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations)
154+
);
155+
l_object_lines.delete;
156+
end if;
157+
158+
l_name := l_names(i);
159+
l_object_lines(l_object_lines.count+1) := l_lines(i);
160+
end loop;
161+
exit when a_sources_cursor%notfound;
156162

157-
l_name := l_names(i);
158-
l_object_lines(l_object_lines.count+1) := l_lines(i);
159163
end loop;
160-
exit when a_sources_cursor%notfound;
161-
162-
end loop;
163-
if a_sources_cursor%rowcount > 0 then
164-
l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type);
165-
ut_annotation_cache_manager.update_cache(
166-
ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations)
167-
);
168-
l_object_lines.delete;
164+
if a_sources_cursor%rowcount > 0 then
165+
l_annotations := ut_annotation_parser.parse_object_annotations(l_object_lines, a_object_type);
166+
ut_annotation_cache_manager.update_cache(
167+
ut_annotated_object(a_object_owner, l_name, a_object_type, l_parse_time, l_annotations)
168+
);
169+
l_object_lines.delete;
170+
end if;
171+
exception
172+
when others then
173+
if a_sources_cursor%isopen then
174+
close a_sources_cursor;
175+
end if;
176+
raise;
177+
end;
178+
if a_sources_cursor%isopen then
179+
close a_sources_cursor;
169180
end if;
170-
close a_sources_cursor;
171181
end;
172182

173183

source/core/ut_utils.pkb

Lines changed: 115 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ create or replace package body ut_utils is
2323
gc_invalid_xml_char constant varchar2(50) := '[^_[:alnum:]\.-]';
2424
gc_full_valid_xml_name constant varchar2(50) := '^([[:alpha:]])([_[:alnum:]\.-])*$';
2525
gc_owner_hash constant integer(11) := dbms_utility.get_hash_value( ut_owner(), 0, power(2,31)-1);
26-
gc_open_chars constant varchar2(4):= chr(91) || chr(123) || chr(40) || chr(60); -- [{( this has very specific purpose to not confuse lexer in IDE
27-
gc_close_chars constant varchar2(4):= chr(93) || chr(125) || chr(41) || chr(62); -- ]})> this has very specific purpose to not confuse lexer in IDE
26+
gc_open_chars constant varchar2(4):= '[{(<';
27+
gc_close_chars constant varchar2(4):= ']})>';
2828
gc_max_plsql_source_len constant integer := 32767;
2929

3030
function surround_with(a_value varchar2, a_quote_char varchar2) return varchar2 is
@@ -660,6 +660,115 @@ create or replace package body ut_utils is
660660
return l_result;
661661
end;
662662

663+
function scan_line(a_line in varchar2, a_in_ml_comment in out boolean) return varchar2 is
664+
-- Normal scan
665+
l_remaining varchar2(32767) := a_line;
666+
l_line varchar2(32767);
667+
l_ml_start binary_integer;
668+
l_comment_start binary_integer;
669+
l_text_start binary_integer;
670+
l_eq_text_start binary_integer;
671+
l_eq_end_char varchar2(1 char);
672+
l_pos binary_integer;
673+
l_end binary_integer;
674+
l_ml_end binary_integer;
675+
begin
676+
if a_in_ml_comment then
677+
l_ml_end := instr(l_remaining, '*/');
678+
if l_ml_end > 0 then
679+
a_in_ml_comment := false;
680+
l_remaining := substr(l_remaining, l_ml_end + 2);
681+
else
682+
return null;
683+
end if;
684+
end if;
685+
686+
loop
687+
exit when l_remaining is null;
688+
l_ml_start := instr(l_remaining, '/*');
689+
l_comment_start := instr(l_remaining, '--');
690+
l_text_start := instr(l_remaining, '''');
691+
-- q' always puts ' at l_text_start; just check the char immediately before it
692+
l_eq_text_start := case
693+
when l_text_start > 1 and substr(l_remaining, l_text_start - 1, 1) = 'q'
694+
then l_text_start - 1
695+
else 0
696+
end;
697+
-- Sentinel gc_max_plsql_source_len means "not present"; 32767 is beyond any VARCHAR2 position
698+
l_pos := least(
699+
case when l_ml_start > 0 then l_ml_start else gc_max_plsql_source_len end,
700+
case when l_comment_start > 0 then l_comment_start else gc_max_plsql_source_len end,
701+
case when l_text_start > 0 then l_text_start else gc_max_plsql_source_len end,
702+
case when l_eq_text_start > 0 then l_eq_text_start else gc_max_plsql_source_len end
703+
);
704+
705+
if l_pos = gc_max_plsql_source_len then
706+
l_line := l_line || l_remaining;
707+
exit;
708+
end if;
709+
710+
l_line := l_line || substr(l_remaining, 1, l_pos - 1);
711+
l_remaining := substr(l_remaining, l_pos);
712+
-- l_remaining now starts exactly at the token; all branch offsets below are relative to 1
713+
if l_pos = l_eq_text_start then
714+
-- q-quoted string: l_remaining starts at 'q', delimiter is at position 3
715+
l_eq_end_char := translate(substr(l_remaining, 3, 1), gc_open_chars, gc_close_chars);
716+
l_end := instr(l_remaining, l_eq_end_char || '''', 4);
717+
if l_end > 0 then
718+
l_line := l_line || substr(l_remaining, 1, l_end + 1);
719+
l_remaining := substr(l_remaining, l_end + 2);
720+
else
721+
l_line := l_line || l_remaining;
722+
exit;
723+
end if;
724+
725+
elsif l_pos = l_ml_start then
726+
-- Multi-line comment: l_remaining starts at '/*', so end search starts at 3
727+
l_ml_end := instr(l_remaining, '*/', 3);
728+
if l_ml_end > 0 then
729+
l_remaining := substr(l_remaining, l_ml_end + 2);
730+
else
731+
a_in_ml_comment := true;
732+
-- preserve trailing newline if present — it belongs to this line, not the comment
733+
if substr(l_remaining, -1) = chr(10) then
734+
l_line := l_line || chr(10);
735+
end if;
736+
return l_line;
737+
end if;
738+
739+
elsif l_pos = l_comment_start then
740+
-- Single-line comment: everything from here is comment, keep and stop
741+
l_line := l_line || l_remaining;
742+
return l_line;
743+
744+
else
745+
-- Regular string literal: l_remaining starts at the opening quote
746+
-- scan from position 2 to skip the opening quote
747+
l_end := 2;
748+
loop
749+
l_end := instr(l_remaining, '''', l_end);
750+
exit when l_end = 0;
751+
if substr(l_remaining, l_end, 2) = '''''' then
752+
l_end := l_end + 2; -- skip escaped quote pair
753+
else
754+
exit; -- real closing quote
755+
end if;
756+
end loop;
757+
758+
if l_end > 0 then
759+
l_line := l_line || substr(l_remaining, 1, l_end);
760+
l_remaining := substr(l_remaining, l_end + 1);
761+
else
762+
l_line := l_line || l_remaining;
763+
exit;
764+
end if;
765+
766+
end if;
767+
end loop;
768+
769+
return l_line;
770+
end;
771+
663772
function replace_multiline_comments(a_source dbms_preprocessor.source_lines_t)
664773
return dbms_preprocessor.source_lines_t
665774
is
@@ -668,13 +777,6 @@ create or replace package body ut_utils is
668777
l_remaining varchar2(32767);
669778
l_in_ml_comment boolean := false;
670779
l_ml_end binary_integer;
671-
l_ml_start binary_integer;
672-
l_comment_start binary_integer;
673-
l_text_start binary_integer;
674-
l_eq_text_start binary_integer;
675-
l_eq_end_char varchar2(1 char);
676-
l_pos binary_integer;
677-
l_end binary_integer;
678780
l_has_ml_comment boolean := false;
679781
begin
680782
if a_source.count = 0 then
@@ -720,92 +822,10 @@ create or replace package body ut_utils is
720822

721823
-- Normal scan
722824
l_remaining := l_line;
723-
l_line := null;
724-
725-
<<scan_line>>
726-
loop
727-
exit when l_remaining is null or l_remaining = '';
728-
l_ml_start := instr(l_remaining, '/*');
729-
l_comment_start := instr(l_remaining, '--');
730-
l_text_start := instr(l_remaining, '''');
731-
-- q' always puts ' at l_text_start; just check the char immediately before it
732-
l_eq_text_start := case
733-
when l_text_start > 1 and substr(l_remaining, l_text_start - 1, 1) = 'q'
734-
then l_text_start - 1
735-
else 0
736-
end;
737-
-- Sentinel gc_max_plsql_source_len means "not present"; 32767 is beyond any VARCHAR2 position
738-
l_pos := least(
739-
case when l_ml_start > 0 then l_ml_start else gc_max_plsql_source_len end,
740-
case when l_comment_start > 0 then l_comment_start else gc_max_plsql_source_len end,
741-
case when l_text_start > 0 then l_text_start else gc_max_plsql_source_len end,
742-
case when l_eq_text_start > 0 then l_eq_text_start else gc_max_plsql_source_len end
743-
);
744-
745-
if l_pos = gc_max_plsql_source_len then
746-
l_line := l_line || l_remaining;
747-
exit scan_line;
748-
end if;
749-
750-
l_line := l_line || substr(l_remaining, 1, l_pos - 1);
751-
l_remaining := substr(l_remaining, l_pos);
752-
-- l_remaining now starts exactly at the token; all branch offsets below are relative to 1
753-
if l_pos = l_eq_text_start then
754-
-- q-quoted string: l_remaining starts at 'q', delimiter is at position 3
755-
l_eq_end_char := translate(substr(l_remaining, 3, 1), gc_open_chars, gc_close_chars);
756-
l_end := instr(l_remaining, l_eq_end_char || '''', 4);
757-
if l_end > 0 then
758-
l_line := l_line || substr(l_remaining, 1, l_end + 1);
759-
l_remaining := substr(l_remaining, l_end + 2);
760-
else
761-
l_line := l_line || l_remaining;
762-
exit scan_line;
763-
end if;
764-
765-
elsif l_pos = l_ml_start then
766-
-- Multi-line comment: l_remaining starts at '/*', so end search starts at 3
767-
l_ml_end := instr(l_remaining, '*/', 3);
768-
if l_ml_end > 0 then
769-
l_remaining := substr(l_remaining, l_ml_end + 2);
770-
else
771-
l_in_ml_comment := true;
772-
-- preserve trailing newline if present — it belongs to this line, not the comment
773-
if substr(l_remaining, -1) = chr(10) then
774-
l_line := l_line || chr(10);
775-
end if;
776-
exit scan_line;
777-
end if;
778-
779-
elsif l_pos = l_comment_start then
780-
-- Single-line comment: everything from here is comment, keep and stop
781-
l_line := l_line || l_remaining;
782-
exit scan_line;
783-
784-
else
785-
-- Regular string literal: l_remaining starts at the opening quote
786-
-- scan from position 2 to skip the opening quote
787-
l_end := 2;
788-
loop
789-
l_end := instr(l_remaining, '''', l_end);
790-
exit when l_end = 0;
791-
if substr(l_remaining, l_end, 2) = '''''' then
792-
l_end := l_end + 2; -- skip escaped quote pair
793-
else
794-
exit; -- real closing quote
795-
end if;
796-
end loop;
797-
798-
if l_end > 0 then
799-
l_line := l_line || substr(l_remaining, 1, l_end);
800-
l_remaining := substr(l_remaining, l_end + 1);
801-
else
802-
l_line := l_line || l_remaining;
803-
exit scan_line;
804-
end if;
805-
806-
end if;
807-
end loop scan_line;
808-
825+
l_line := scan_line(l_remaining, l_in_ml_comment);
826+
if l_line is null then
827+
l_line := '';
828+
end if;
809829
l_result(i) := l_line;
810830
end loop;
811831

0 commit comments

Comments
 (0)