1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.maven.jxr;
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51 import java.io.BufferedReader;
52 import java.io.FileInputStream;
53 import java.io.FileOutputStream;
54 import java.io.FileReader;
55 import java.io.FileWriter;
56 import java.io.IOException;
57 import java.io.InputStreamReader;
58 import java.io.ObjectInputStream;
59 import java.io.ObjectOutputStream;
60 import java.io.OutputStreamWriter;
61 import java.io.PrintWriter;
62 import java.io.Reader;
63 import java.io.Serializable;
64 import java.io.Writer;
65 import java.nio.file.Files;
66 import java.nio.file.Path;
67 import java.util.Collections;
68 import java.util.HashSet;
69 import java.util.Hashtable;
70 import java.util.List;
71 import java.util.Locale;
72 import java.util.Map;
73 import java.util.Set;
74
75 import org.apache.maven.jxr.pacman.ClassType;
76 import org.apache.maven.jxr.pacman.FileManager;
77 import org.apache.maven.jxr.pacman.ImportType;
78 import org.apache.maven.jxr.pacman.JavaFile;
79 import org.apache.maven.jxr.pacman.PackageManager;
80 import org.apache.maven.jxr.pacman.PackageType;
81 import org.apache.maven.jxr.util.SimpleWordTokenizer;
82 import org.apache.maven.jxr.util.StringEntry;
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111 public class JavaCodeTransform implements Serializable {
112
113
114
115
116
117
118
119 private static final boolean LINE_NUMBERS = true;
120
121
122
123
124 private static final String COMMENT_START = "<em class=\"jxr_comment\">";
125
126
127
128
129 private static final String COMMENT_END = "</em>";
130
131
132
133
134 private static final String JAVADOC_COMMENT_START = "<em class=\"jxr_javadoccomment\">";
135
136
137
138
139 private static final String JAVADOC_COMMENT_END = "</em>";
140
141
142
143
144 private static final String STRING_START = "<span class=\"jxr_string\">";
145
146
147
148
149 private static final String STRING_END = "</span>";
150
151
152
153
154 private static final String RESERVED_WORD_START = "<strong class=\"jxr_keyword\">";
155
156
157
158
159 private static final String RESERVED_WORD_END = "</strong>";
160
161
162
163
164 private static final String STYLESHEET_FILENAME = "stylesheet.css";
165
166 private static final String[] VALID_URI_SCHEMES = {"http://", "https://", "mailto:"};
167
168
169
170
171
172 private static final char[] VALID_URI_CHARS = {
173 '?', '+', '%', '&', ':', '/', '.', '@', '_', ';', '=', '$', ',', '-', '!', '~', '*', '\'', '(', ')'
174 };
175
176
177
178
179
180
181
182
183 private Map<String, String> reservedWords = new Hashtable<>();
184
185
186
187
188 private boolean inMultiLineComment = false;
189
190
191
192
193 private boolean inJavadocComment = false;
194
195
196
197
198 private Path currentFilename = null;
199
200
201
202
203 private String revision = null;
204
205
206
207
208 private String outputEncoding = null;
209
210
211
212
213 private Locale locale = null;
214
215
216
217
218 private Path javadocLinkDir;
219
220
221
222
223 private final PackageManager packageManager;
224
225
226
227
228 private final FileManager fileManager;
229
230 {
231 reservedWords.put("abstract", "abstract");
232 reservedWords.put("do", "do");
233 reservedWords.put("inner", "inner");
234 reservedWords.put("public", "public");
235 reservedWords.put("var", "var");
236 reservedWords.put("boolean", "boolean");
237 reservedWords.put("continue", "continue");
238 reservedWords.put("int", "int");
239 reservedWords.put("return", "return");
240 reservedWords.put("void", "void");
241 reservedWords.put("break", "break");
242 reservedWords.put("else", "else");
243 reservedWords.put("interface", "interface");
244 reservedWords.put("short", "short");
245 reservedWords.put("volatile", "volatile");
246 reservedWords.put("byvalue", "byvalue");
247 reservedWords.put("extends", "extends");
248 reservedWords.put("long", "long");
249 reservedWords.put("static", "static");
250 reservedWords.put("while", "while");
251 reservedWords.put("case", "case");
252 reservedWords.put("final", "final");
253 reservedWords.put("native", "native");
254 reservedWords.put("super", "super");
255 reservedWords.put("transient", "transient");
256 reservedWords.put("cast", "cast");
257 reservedWords.put("float", "float");
258 reservedWords.put("new", "new");
259 reservedWords.put("rest", "rest");
260 reservedWords.put("catch", "catch");
261 reservedWords.put("for", "for");
262 reservedWords.put("null", "null");
263 reservedWords.put("synchronized", "synchronized");
264 reservedWords.put("char", "char");
265 reservedWords.put("finally", "finally");
266 reservedWords.put("operator", "operator");
267 reservedWords.put("this", "this");
268 reservedWords.put("class", "class");
269 reservedWords.put("generic", "generic");
270 reservedWords.put("outer", "outer");
271 reservedWords.put("switch", "switch");
272 reservedWords.put("const", "const");
273 reservedWords.put("goto", "goto");
274 reservedWords.put("package", "package");
275 reservedWords.put("throw", "throw");
276 reservedWords.put("double", "double");
277 reservedWords.put("if", "if");
278 reservedWords.put("private", "private");
279 reservedWords.put("true", "true");
280 reservedWords.put("default", "default");
281 reservedWords.put("import", "import");
282 reservedWords.put("protected", "protected");
283 reservedWords.put("try", "try");
284 reservedWords.put("throws", "throws");
285 reservedWords.put("implements", "implements");
286 }
287
288 public JavaCodeTransform(PackageManager packageManager, FileManager fileManager) {
289 this.packageManager = packageManager;
290 this.fileManager = fileManager;
291 }
292
293
294
295
296
297
298
299
300
301
302
303 private String syntaxHighlight(String line) {
304 return htmlFilter(line);
305 }
306
307
308
309
310
311
312 private void appendHeader(PrintWriter out) {
313 String outputEncoding = this.outputEncoding;
314 if (outputEncoding == null) {
315 outputEncoding = "ISO-8859-1";
316 }
317
318
319 out.println("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" "
320 + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">");
321 out.print("<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"");
322 out.print(locale);
323 out.print("\" lang=\"");
324 out.print(locale);
325 out.println("\">");
326 out.print("<head>");
327 out.print("<meta http-equiv=\"content-type\" content=\"text/html; charset=");
328 out.print(outputEncoding);
329 out.println("\" />");
330
331
332 out.print("<title>");
333 try {
334 JavaFile javaFile = fileManager.getFile(this.getCurrentFilename());
335
336 if (javaFile.getClassType() != null && javaFile.getClassType().getFilename() != null) {
337 out.print(javaFile.getClassType().getFilename());
338 } else {
339 out.print(javaFile.getFilename());
340 }
341 out.print(' ');
342 } catch (IOException e) {
343 e.printStackTrace();
344 } finally {
345 out.println("xref</title>");
346 }
347
348
349 out.print("<link type=\"text/css\" rel=\"stylesheet\" href=\"");
350 out.print(this.getPackageRoot());
351 out.print(STYLESHEET_FILENAME);
352 out.println("\" />");
353
354 out.println("</head>");
355 out.println("<body>");
356 out.print(this.getFileOverview());
357
358
359 out.println("<pre>");
360 }
361
362
363
364
365
366
367
368 private void appendFooter(PrintWriter out, String bottom) {
369 out.println("</pre>");
370 out.println("<hr/>");
371 out.print("<div id=\"footer\">");
372 out.print(bottom);
373 out.println("</div>");
374 out.println("</body>");
375 out.println("</html>");
376 }
377
378
379
380
381
382
383
384
385
386
387
388
389
390 private void transform(
391 Reader sourceReader,
392 Writer destWriter,
393 Locale locale,
394 String outputEncoding,
395 Path javadocLinkDir,
396 String revision,
397 String bottom)
398 throws IOException {
399 this.locale = locale;
400 this.outputEncoding = outputEncoding;
401 this.javadocLinkDir = javadocLinkDir;
402 this.revision = revision;
403
404 BufferedReader in = new BufferedReader(sourceReader);
405
406 PrintWriter out = new PrintWriter(destWriter);
407
408 String line;
409
410 appendHeader(out);
411
412 int linenumber = 1;
413 while ((line = in.readLine()) != null) {
414 if (LINE_NUMBERS) {
415 out.print("<a class=\"jxr_linenumber\" name=\"L" + linenumber + "\" " + "href=\"#L" + linenumber + "\">"
416 + linenumber + "</a>" + getLineWidth(linenumber));
417 }
418
419 out.println(this.syntaxHighlight(line));
420
421 ++linenumber;
422 }
423
424 appendFooter(out, bottom);
425
426 out.flush();
427 }
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442 public final void transform(
443 Path sourcefile,
444 Path destfile,
445 Locale locale,
446 String inputEncoding,
447 String outputEncoding,
448 Path javadocLinkDir,
449 String revision,
450 String bottom)
451 throws IOException {
452 this.setCurrentFilename(sourcefile);
453
454
455 Files.createDirectories(destfile.getParent());
456
457 try (Reader fr = getReader(sourcefile, inputEncoding);
458 Writer fw = getWriter(destfile, outputEncoding)) {
459 transform(fr, fw, locale, outputEncoding, javadocLinkDir, revision, bottom);
460 } catch (RuntimeException e) {
461 System.out.println("Unable to processPath " + sourcefile + " => " + destfile);
462 throw e;
463 }
464 }
465
466 private Writer getWriter(Path destfile, String outputEncoding) throws IOException {
467 Writer fw;
468 if (outputEncoding != null) {
469 fw = new OutputStreamWriter(new FileOutputStream(destfile.toFile()), outputEncoding);
470 } else {
471 fw = new FileWriter(destfile.toFile());
472 }
473 return fw;
474 }
475
476 private Reader getReader(Path sourcefile, String inputEncoding) throws IOException {
477 Reader fr;
478 if (inputEncoding != null) {
479 fr = new InputStreamReader(new FileInputStream(sourcefile.toFile()), inputEncoding);
480 } else {
481 fr = new FileReader(sourcefile.toFile());
482 }
483 return fr;
484 }
485
486
487
488
489
490
491 private Path getCurrentFilename() {
492 return this.currentFilename;
493 }
494
495
496
497
498
499
500 private void setCurrentFilename(Path filename) {
501 this.currentFilename = filename;
502 }
503
504
505
506
507
508
509 private String getPackageRoot() {
510 StringBuilder buff = new StringBuilder();
511
512 JavaFile jf;
513
514 try {
515 jf = fileManager.getFile(this.getCurrentFilename());
516 } catch (IOException e) {
517 e.printStackTrace();
518 return null;
519 }
520
521 String current = jf.getPackageType().getName();
522
523 int count = this.getPackageCount(current);
524
525 for (int i = 0; i < count; ++i) {
526 buff.append("../");
527 }
528
529 return buff.toString();
530 }
531
532
533
534
535
536
537
538 private String uriFilter(String line) {
539 for (String scheme : VALID_URI_SCHEMES) {
540 int index = line.indexOf(scheme);
541
542 if (index != -1) {
543 int start = index;
544 int end = -1;
545
546 for (int j = start; j < line.length(); ++j) {
547 char current = line.charAt(j);
548
549 if (!Character.isLetterOrDigit(current) && isInvalidURICharacter(current)) {
550 end = j;
551 break;
552 }
553
554 end = j;
555 }
556
557
558
559
560 if (end != -1) {
561 String uri = (end + 1 == line.length()) ? line.substring(start) : line.substring(start, end);
562
563 line = line.replace(uri, "<a href=\"" + uri + "\" target=\"alexandria_uri\">" + uri + "</a>");
564 }
565 }
566 }
567
568
569 if (!inMultiLineComment && !inJavadocComment) {
570 return jxrFilter(line);
571 }
572
573 return line;
574 }
575
576
577
578
579
580
581 public final String getRevision() {
582 return this.revision;
583 }
584
585
586
587
588
589
590
591
592
593 private String xrLine(String line, String packageName, ClassType classType) {
594 StringBuilder buff = new StringBuilder(line);
595
596 String link;
597 String find;
598 String href;
599
600 if (classType != null) {
601 href = this.getHREF(packageName, classType);
602 find = classType.getName();
603
604
605 link = "<a name=\"" + find + "\" href=\"" + href + "\">" + find + "</a>";
606 } else {
607 href = this.getHREF(packageName);
608 find = packageName;
609
610
611 link = "<a href=\"" + href + "\">" + find + "</a>";
612 }
613
614
615
616
617
618
619 String replace = link;
620 List<StringEntry> tokens = SimpleWordTokenizer.tokenize(buff.toString(), find);
621
622
623
624
625
626 Collections.reverse(tokens);
627
628 for (StringEntry token : tokens) {
629 int start = token.getIndex();
630 int end = token.getIndex() + find.length();
631
632 buff.replace(start, end, replace);
633 }
634
635 return buff.toString();
636 }
637
638
639
640
641
642
643
644
645
646
647
648 private String htmlFilter(String line) {
649 if (line == null || line.equals("")) {
650 return "";
651 }
652 line = line.replace("&", "&")
653 .replace("<", "<")
654 .replace(">", ">")
655 .replace("\\\\", "\\")
656 .replace("\\\"", "\\"")
657 .replace("'\"'", "'"'");
658 return ongoingMultiLineCommentFilter(line);
659 }
660
661
662
663
664
665
666
667
668
669 private String ongoingMultiLineCommentFilter(String line) {
670 if (line == null || line.equals("")) {
671 return "";
672 }
673 final String[] tags = inJavadocComment
674 ? new String[] {JAVADOC_COMMENT_START, JAVADOC_COMMENT_END}
675 : inMultiLineComment ? new String[] {COMMENT_START, COMMENT_END} : null;
676
677 if (tags == null) {
678
679 return inlineCommentFilter(line);
680 }
681
682 int index = line.indexOf("*/");
683
684
685 String comment = uriFilter(index < 0 ? line : line.substring(0, index));
686 if (index >= 0) {
687 inJavadocComment = false;
688 inMultiLineComment = false;
689 }
690 StringBuilder buf = new StringBuilder(tags[0]).append(comment);
691
692 if (index >= 0) {
693 buf.append("*/");
694 }
695 buf.append(tags[1]);
696
697 if (index >= 0 && line.length() > index + 2) {
698 buf.append(inlineCommentFilter(line.substring(index + 2)));
699 }
700 return buf.toString();
701 }
702
703
704
705
706
707
708
709
710
711
712 private String inlineCommentFilter(String line) {
713
714
715
716 if (line == null || line.equals("")) {
717 return "";
718 }
719 int index = line.indexOf("//");
720 if ((index >= 0) && !isInsideString(line, index)) {
721 return beginMultiLineCommentFilter(line.substring(0, index))
722 + COMMENT_START
723 + line.substring(index)
724 + COMMENT_END;
725 }
726
727 return beginMultiLineCommentFilter(line);
728 }
729
730
731
732
733
734
735
736
737 private String beginMultiLineCommentFilter(String line) {
738
739
740
741 if (line == null || line.equals("")) {
742 return "";
743 }
744
745 int index = line.indexOf("/*");
746
747 if ((index > -1) && !isInsideString(line, index)) {
748 String fromIndex = line.substring(index);
749 if (fromIndex.startsWith("/**") && !(fromIndex.startsWith("/**/"))) {
750 inJavadocComment = true;
751 } else {
752 inMultiLineComment = true;
753 }
754
755
756
757
758 return stringFilter(line.substring(0, index)) + ongoingMultiLineCommentFilter(fromIndex);
759 }
760
761
762
763 else {
764 return stringFilter(line);
765 }
766 }
767
768
769
770
771
772
773
774 private String stringFilter(String line) {
775 if (line == null || line.equals("")) {
776 return "";
777 }
778 StringBuilder buf = new StringBuilder();
779 if (line.indexOf('"') <= -1) {
780 return keywordFilter(line);
781 }
782 int start = 0;
783 int startStringIndex = -1;
784 int endStringIndex = -1;
785 int tempIndex;
786
787 while ((tempIndex = line.indexOf('"')) > -1) {
788
789 if (startStringIndex == -1) {
790 startStringIndex = 0;
791 buf.append(stringFilter(line.substring(start, tempIndex)));
792 buf.append(STRING_START).append('"');
793 line = line.substring(tempIndex + 1);
794 }
795
796 else {
797 startStringIndex = -1;
798 endStringIndex = tempIndex;
799 buf.append(line, 0, endStringIndex + 1);
800 buf.append(STRING_END);
801 line = line.substring(endStringIndex + 1);
802 }
803 }
804
805 buf.append(keywordFilter(line));
806
807 return buf.toString();
808 }
809
810
811
812
813
814
815
816 private String keywordFilter(String line) {
817 final String classKeyword = "class";
818
819 if (line == null || line.equals("")) {
820 return "";
821 }
822 StringBuilder buf = new StringBuilder();
823 int i = 0;
824 char ch;
825 StringBuilder temp = new StringBuilder();
826 while (i < line.length()) {
827 temp.setLength(0);
828 ch = line.charAt(i);
829 while (i < line.length() && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'))) {
830 temp.append(ch);
831 i++;
832 if (i < line.length()) {
833 ch = line.charAt(i);
834 }
835 }
836 String tempString = temp.toString();
837
838
839 if (classKeyword.equals(tempString) && ch == '=') {
840 i++;
841 } else if (reservedWords.containsKey(tempString)) {
842 line = line.substring(0, i - tempString.length())
843 + RESERVED_WORD_START
844 + tempString
845 + RESERVED_WORD_END
846 + line.substring(i);
847 i += (RESERVED_WORD_START.length() + RESERVED_WORD_END.length());
848 } else {
849 i++;
850 }
851 }
852 buf.append(line);
853
854 return uriFilter(buf.toString());
855 }
856
857
858
859
860
861
862
863
864
865 private boolean isInsideString(String line, int position) {
866 if (line.indexOf('"') < 0) {
867 return false;
868 }
869 int index;
870 String left = line.substring(0, position);
871 String right = line.substring(position);
872 int leftCount = 0;
873 int rightCount = 0;
874 while ((index = left.indexOf('"')) > -1) {
875 leftCount++;
876 left = left.substring(index + 1);
877 }
878 while ((index = right.indexOf('"')) > -1) {
879 rightCount++;
880 right = right.substring(index + 1);
881 }
882 return (rightCount % 2 != 0 && leftCount % 2 != 0);
883 }
884
885
886
887
888
889 final void writeObject(ObjectOutputStream oos) throws IOException {
890 oos.defaultWriteObject();
891 }
892
893
894
895
896
897
898 final void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
899 ois.defaultReadObject();
900 }
901
902
903
904
905
906
907 private String getFileOverview() {
908 StringBuilder overview = new StringBuilder();
909
910
911 if (javadocLinkDir != null) {
912 overview.append("<div id=\"overview\">");
913
914 Path javadocURI;
915
916 try {
917 JavaFile jf = fileManager.getFile(this.getCurrentFilename());
918
919 javadocURI =
920 javadocLinkDir.resolve(jf.getPackageType().getName().replace('.', '/'));
921
922 String fileName;
923 if (jf.getClassType() != null && jf.getClassType().getFilename() != null) {
924 fileName = jf.getClassType().getFilename();
925 } else {
926 fileName = jf.getFilename();
927 }
928 javadocURI = javadocURI.resolve(fileName + ".html");
929
930 String javadocHREF = "<a href=\"" + javadocURI.toString().replace('\\', '/') + "\">View Javadoc</a>";
931
932
933 overview.append(javadocHREF);
934 } catch (IOException e) {
935 e.printStackTrace();
936 }
937
938 overview.append("</div>");
939 }
940
941 return overview.toString();
942 }
943
944
945
946
947
948
949
950 private String getLineWidth(int linenumber) {
951 if (linenumber < 10) {
952 return " ";
953 } else if (linenumber < 100) {
954 return " ";
955 } else {
956 return " ";
957 }
958 }
959
960
961
962
963
964
965
966 private String jxrFilter(String line) {
967 JavaFile jf;
968
969 try {
970
971 if (this.getCurrentFilename() == null) {
972 return line;
973 }
974
975 jf = fileManager.getFile(this.getCurrentFilename());
976 } catch (IOException e) {
977 e.printStackTrace();
978 return line;
979 }
980
981 Set<String> packages = new HashSet<>();
982
983
984 for (ImportType importType : jf.getImportTypes()) {
985 packages.add(importType.getPackage());
986 }
987
988
989 packages.add(jf.getPackageType().getName());
990
991 List<StringEntry> words = SimpleWordTokenizer.tokenize(line);
992
993
994 for (StringEntry word : words) {
995 for (String pkg : packages) {
996
997
998
999 PackageType currentImport = packageManager.getPackageType(pkg);
1000
1001
1002
1003
1004
1005 if (currentImport == null) {
1006 continue;
1007 }
1008
1009
1010
1011
1012
1013
1014 String wordName = word.toString();
1015
1016 if (wordName.indexOf('.') != -1) {
1017
1018
1019
1020 String fqpnPackage = wordName.substring(0, wordName.lastIndexOf('.'));
1021 String fqpnClass = wordName.substring(wordName.lastIndexOf('.') + 1);
1022
1023
1024
1025
1026
1027 PackageType pt = packageManager.getPackageType(fqpnPackage);
1028
1029 if (pt != null) {
1030 ClassType ct = pt.getClassType(fqpnClass);
1031
1032 if (ct != null) {
1033
1034
1035
1036
1037 line = xrLine(line, pt.getName(), ct);
1038 }
1039 }
1040
1041 if (fqpnPackage.equals(currentImport.getName()) && currentImport.getClassType(fqpnClass) != null) {
1042
1043
1044 line = xrLine(line, pkg, currentImport.getClassType(fqpnClass));
1045 }
1046 } else if (currentImport.getClassType(wordName) != null) {
1047 line = xrLine(line, pkg, currentImport.getClassType(wordName));
1048 }
1049 }
1050 }
1051
1052 return importFilter(line);
1053 }
1054
1055
1056
1057
1058
1059
1060
1061
1062 private String getHREF(String dest, ClassType jc) {
1063 StringBuilder href = new StringBuilder();
1064
1065
1066 href.append(this.getPackageRoot());
1067
1068
1069 dest = dest.replace(".*", "").replace('.', '/');
1070
1071 href.append(dest);
1072
1073
1074 if (jc != null) {
1075 href.append('/');
1076 href.append(jc.getFilename());
1077 href.append(".html");
1078 href.append('#');
1079 href.append(jc.getName());
1080 }
1081
1082 return href.toString();
1083 }
1084
1085
1086
1087
1088
1089
1090
1091 private String getHREF(String dest) {
1092 return getHREF(dest, null);
1093 }
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106 private int getPackageCount(String packageName) {
1107 if (packageName == null) {
1108 return 0;
1109 }
1110
1111 int count = 0;
1112 int index = 0;
1113
1114 while (true) {
1115 index = packageName.indexOf('.', index);
1116
1117 if (index == -1) {
1118 break;
1119 }
1120 ++index;
1121 ++count;
1122 }
1123
1124
1125 ++count;
1126
1127 return count;
1128 }
1129
1130
1131
1132
1133
1134
1135
1136 private String importFilter(String line) {
1137 int start = -1;
1138
1139
1140
1141
1142
1143
1144 boolean isPackage = line.trim().startsWith("package ");
1145 boolean isImport = line.trim().startsWith("import ");
1146
1147 if (isImport || isPackage) {
1148 start = line.trim().indexOf(' ');
1149 }
1150
1151 if (start != -1) {
1152
1153 String pkg = line.substring(start).trim();
1154
1155
1156 String classname = null;
1157
1158 if (pkg.contains(".*")) {
1159 pkg = pkg.replace(".*", "");
1160 } else if (!isPackage) {
1161
1162
1163 String packageLine = pkg;
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173 int a = packageLine.lastIndexOf('.') + 1;
1174 int b = packageLine.length() - 1;
1175
1176 if (a > b + 1) {
1177 classname = packageLine.substring(packageLine.lastIndexOf('.') + 1, packageLine.length() - 1);
1178
1179 int end = pkg.lastIndexOf('.');
1180 if (end == -1) {
1181 end = pkg.length() - 1;
1182 }
1183
1184 pkg = pkg.substring(0, end);
1185 }
1186 }
1187
1188 pkg = pkg.replace(";", "");
1189 String pkgHREF = getHREF(pkg);
1190
1191
1192 if (packageManager.getPackageType(pkg) != null || isPackage) {
1193
1194 if (classname != null) {
1195 line = line.replace(
1196 classname, "<a href=\"" + pkgHREF + '/' + classname + ".html" + "\">" + classname + "</a>");
1197 }
1198
1199
1200 line = line.replace(pkg, "<a href=\"" + pkgHREF + '/' + DirectoryIndexer.INDEX + "\">" + pkg + "</a>");
1201 }
1202 }
1203
1204 return line;
1205 }
1206
1207
1208
1209
1210
1211
1212
1213 private boolean isInvalidURICharacter(char c) {
1214 for (char validUriChar : VALID_URI_CHARS) {
1215 if (validUriChar == c) {
1216 return false;
1217 }
1218 }
1219
1220 return true;
1221 }
1222 }