1 /*
2  * Copyright (C) 2019 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 <android-base/logging.h>
18 #include <android-base/strings.h>
19 #include <hidl-util/FQName.h>
20 #include <hidl-util/Formatter.h>
21 #include <hidl-util/StringHelper.h>
22 #include <set>
23 #include <string>
24 #include <vector>
25 
26 #include "AidlHelper.h"
27 #include "ArrayType.h"
28 #include "Coordinator.h"
29 #include "Interface.h"
30 #include "Method.h"
31 #include "NamedType.h"
32 #include "Reference.h"
33 #include "Scope.h"
34 
35 namespace android {
36 
37 Formatter* AidlHelper::notesFormatter = nullptr;
38 
notes()39 Formatter& AidlHelper::notes() {
40     CHECK(notesFormatter != nullptr);
41     return *notesFormatter;
42 }
43 
setNotes(Formatter * formatter)44 void AidlHelper::setNotes(Formatter* formatter) {
45     CHECK(formatter != nullptr);
46     notesFormatter = formatter;
47 }
48 
getAidlName(const FQName & fqName)49 std::string AidlHelper::getAidlName(const FQName& fqName) {
50     std::vector<std::string> names;
51     for (const std::string& name : fqName.names()) {
52         names.push_back(StringHelper::Capitalize(name));
53     }
54     return StringHelper::JoinStrings(names, "");
55 }
56 
getAidlPackage(const FQName & fqName)57 std::string AidlHelper::getAidlPackage(const FQName& fqName) {
58     std::string aidlPackage = fqName.package();
59     if (fqName.getPackageMajorVersion() != 1) {
60         aidlPackage += std::to_string(fqName.getPackageMajorVersion());
61     }
62 
63     return aidlPackage;
64 }
65 
getAidlFQName(const FQName & fqName)66 std::string AidlHelper::getAidlFQName(const FQName& fqName) {
67     return getAidlPackage(fqName) + "." + getAidlName(fqName);
68 }
69 
importLocallyReferencedType(const Type & type,std::set<std::string> * imports)70 void AidlHelper::importLocallyReferencedType(const Type& type, std::set<std::string>* imports) {
71     if (type.isArray()) {
72         return importLocallyReferencedType(*static_cast<const ArrayType*>(&type)->getElementType(),
73                                            imports);
74     }
75     if (type.isTemplatedType()) {
76         return importLocallyReferencedType(
77                 *static_cast<const TemplatedType*>(&type)->getElementType(), imports);
78     }
79 
80     if (!type.isNamedType()) return;
81     const NamedType& namedType = *static_cast<const NamedType*>(&type);
82 
83     std::string import = AidlHelper::getAidlFQName(namedType.fqName());
84     imports->insert(import);
85 }
86 
87 // This tries iterating over the HIDL AST which is a bit messy because
88 // it has to encode the logic in the rest of hidl2aidl. It would be better
89 // if we could iterate over the AIDL structure which has already been
90 // processed.
emitFileHeader(Formatter & out,const NamedType & type)91 void AidlHelper::emitFileHeader(Formatter& out, const NamedType& type) {
92     out << "// FIXME: license file if you have one\n\n";
93     out << "package " << getAidlPackage(type.fqName()) << ";\n\n";
94 
95     std::set<std::string> imports;
96 
97     // Import all the defined types since they will now be in a different file
98     if (type.isScope()) {
99         const Scope& scope = static_cast<const Scope&>(type);
100         for (const NamedType* namedType : scope.getSubTypes()) {
101             importLocallyReferencedType(*namedType, &imports);
102         }
103     }
104 
105     // Import all the referenced types
106     if (type.isInterface()) {
107         // This is a separate case because getReferences doesn't traverse all the superTypes and
108         // sometimes includes references to types that would not exist on AIDL
109         const std::vector<const Method*>& methods =
110                 getUserDefinedMethods(static_cast<const Interface&>(type));
111         for (const Method* method : methods) {
112             for (const Reference<Type>* ref : method->getReferences()) {
113                 importLocallyReferencedType(*ref->get(), &imports);
114             }
115         }
116     } else {
117         for (const Reference<Type>* ref : type.getReferences()) {
118             if (ref->get()->definedName() == type.fqName().name()) {
119                 // Don't import the referenced type if this is referencing itself
120                 continue;
121             }
122             importLocallyReferencedType(*ref->get(), &imports);
123         }
124     }
125 
126     for (const std::string& import : imports) {
127         out << "import " << import << ";\n";
128     }
129 
130     if (imports.size() > 0) {
131         out << "\n";
132     }
133 }
134 
getFileWithHeader(const NamedType & namedType,const Coordinator & coordinator)135 Formatter AidlHelper::getFileWithHeader(const NamedType& namedType,
136                                         const Coordinator& coordinator) {
137     std::string aidlPackage = getAidlPackage(namedType.fqName());
138     Formatter out = coordinator.getFormatter(namedType.fqName(), Coordinator::Location::DIRECT,
139                                              base::Join(base::Split(aidlPackage, "."), "/") + "/" +
140                                                      getAidlName(namedType.fqName()) + ".aidl");
141     emitFileHeader(out, namedType);
142     return out;
143 }
144 
145 }  // namespace android
146