1 /*
<lambda>null2  * 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 package com.android.protolog.tool
18 
19 import com.android.internal.protolog.common.LogLevel
20 import com.github.javaparser.ast.CompilationUnit
21 import com.github.javaparser.ast.ImportDeclaration
22 import com.github.javaparser.ast.expr.BinaryExpr
23 import com.github.javaparser.ast.expr.Expression
24 import com.github.javaparser.ast.expr.StringLiteralExpr
25 import java.util.UUID
26 
27 object CodeUtils {
28     /**
29      * Returns a stable hash of a string.
30      * We reimplement String::hashCode() for readability reasons.
31      */
32     fun hash(
33         position: String,
34         messageString: String,
35         logLevel: LogLevel,
36         logGroup: LogGroup
37     ): Long {
38         val fullStringIdentifier = position + messageString + logLevel.name + logGroup.name
39         return UUID.nameUUIDFromBytes(fullStringIdentifier.toByteArray()).mostSignificantBits
40     }
41 
42     fun checkWildcardStaticImported(code: CompilationUnit, className: String, fileName: String) {
43         code.findAll(ImportDeclaration::class.java)
44                 .forEach { im ->
45                     if (im.isStatic && im.isAsterisk && im.name.toString() == className) {
46                         throw IllegalImportException("Wildcard static imports of $className " +
47                                 "methods are not supported.", ParsingContext(fileName, im))
48                     }
49                 }
50     }
51 
52     fun isClassImportedOrSamePackage(code: CompilationUnit, className: String): Boolean {
53         val packageName = className.substringBeforeLast('.')
54         return code.packageDeclaration.isPresent &&
55                 code.packageDeclaration.get().nameAsString == packageName ||
56                 code.findAll(ImportDeclaration::class.java)
57                         .any { im ->
58                             !im.isStatic &&
59                                     ((!im.isAsterisk && im.name.toString() == className) ||
60                                             (im.isAsterisk && im.name.toString() == packageName))
61                         }
62     }
63 
64     fun staticallyImportedMethods(code: CompilationUnit, className: String): Set<String> {
65         return code.findAll(ImportDeclaration::class.java)
66                 .filter { im ->
67                     im.isStatic &&
68                             im.name.toString().substringBeforeLast('.') == className
69                 }
70                 .map { im -> im.name.toString().substringAfterLast('.') }.toSet()
71     }
72 
73     fun concatMultilineString(expr: Expression, context: ParsingContext): String {
74         return when (expr) {
75             is StringLiteralExpr -> expr.asString()
76             is BinaryExpr -> when {
77                 expr.operator == BinaryExpr.Operator.PLUS ->
78                     concatMultilineString(expr.left, context) +
79                             concatMultilineString(expr.right, context)
80                 else -> throw InvalidProtoLogCallException(
81                         "expected a string literal " +
82                                 "or concatenation of string literals, got: $expr", context)
83             }
84             else -> throw InvalidProtoLogCallException("expected a string literal " +
85                     "or concatenation of string literals, got: $expr", context)
86         }
87     }
88 }
89