1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "java/AnnotationProcessor.h"
18 
19 #include <algorithm>
20 #include <array>
21 
22 #include "text/Unicode.h"
23 #include "text/Utf8Iterator.h"
24 #include "util/Util.h"
25 
26 using ::aapt::text::Printer;
27 using ::aapt::text::Utf8Iterator;
28 using ::android::StringPiece;
29 
30 namespace aapt {
31 
ExtractFirstSentence(const StringPiece & comment)32 StringPiece AnnotationProcessor::ExtractFirstSentence(const StringPiece& comment) {
33   Utf8Iterator iter(comment);
34   while (iter.HasNext()) {
35     const char32_t codepoint = iter.Next();
36     if (codepoint == U'.') {
37       const size_t current_position = iter.Position();
38       if (!iter.HasNext() || text::IsWhitespace(iter.Next())) {
39         return comment.substr(0, current_position);
40       }
41     }
42   }
43   return comment;
44 }
45 
46 struct AnnotationRule {
47   enum : uint32_t {
48     kDeprecated = 0x01,
49     kSystemApi = 0x02,
50     kTestApi = 0x04,
51   };
52 
53   StringPiece doc_str;
54   uint32_t bit_mask;
55   StringPiece annotation;
56 };
57 
58 static std::array<AnnotationRule, 2> sAnnotationRules = {{
59     {"@SystemApi", AnnotationRule::kSystemApi, "@android.annotation.SystemApi"},
60     {"@TestApi", AnnotationRule::kTestApi, "@android.annotation.TestApi"},
61 }};
62 
AppendCommentLine(std::string comment)63 void AnnotationProcessor::AppendCommentLine(std::string comment) {
64   static const std::string sDeprecated = "@deprecated";
65 
66   // Treat deprecated specially, since we don't remove it from the source comment.
67   if (comment.find(sDeprecated) != std::string::npos) {
68     annotation_bit_mask_ |= AnnotationRule::kDeprecated;
69   }
70 
71   for (const AnnotationRule& rule : sAnnotationRules) {
72     std::string::size_type idx = comment.find(rule.doc_str.data());
73     if (idx != std::string::npos) {
74       annotation_bit_mask_ |= rule.bit_mask;
75       comment.erase(comment.begin() + idx, comment.begin() + idx + rule.doc_str.size());
76     }
77   }
78 
79   // Check if after removal of annotations the line is empty.
80   const StringPiece trimmed = util::TrimWhitespace(comment);
81   if (trimmed.empty()) {
82     return;
83   }
84 
85   // If there was trimming to do, copy the string.
86   if (trimmed.size() != comment.size()) {
87     comment = trimmed.to_string();
88   }
89 
90   if (!has_comments_) {
91     has_comments_ = true;
92     comment_ << "/**";
93   }
94   comment_ << "\n * " << std::move(comment);
95 }
96 
AppendComment(const StringPiece & comment)97 void AnnotationProcessor::AppendComment(const StringPiece& comment) {
98   // We need to process line by line to clean-up whitespace and append prefixes.
99   for (StringPiece line : util::Tokenize(comment, '\n')) {
100     line = util::TrimWhitespace(line);
101     if (!line.empty()) {
102       AppendCommentLine(line.to_string());
103     }
104   }
105 }
106 
AppendNewLine()107 void AnnotationProcessor::AppendNewLine() {
108   if (has_comments_) {
109     comment_ << "\n *";
110   }
111 }
112 
Print(Printer * printer,bool strip_api_annotations) const113 void AnnotationProcessor::Print(Printer* printer, bool strip_api_annotations) const {
114   if (has_comments_) {
115     std::string result = comment_.str();
116     for (const StringPiece& line : util::Tokenize(result, '\n')) {
117       printer->Println(line);
118     }
119     printer->Println(" */");
120   }
121 
122   if (annotation_bit_mask_ & AnnotationRule::kDeprecated) {
123     printer->Println("@Deprecated");
124   }
125 
126   if (strip_api_annotations) {
127     return;
128   }
129   for (const AnnotationRule& rule : sAnnotationRules) {
130     if (annotation_bit_mask_ & rule.bit_mask) {
131       printer->Println(rule.annotation);
132     }
133   }
134 }
135 
136 }  // namespace aapt
137