1 /* 2 * Copyright (C) 2018 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.tools.metalava 18 19 import com.android.SdkConstants.DOT_TXT 20 import com.android.SdkConstants.DOT_XML 21 22 /** File formats that metalava can emit APIs to */ 23 enum class FileFormat(val description: String, val version: String? = null) { 24 UNKNOWN("?"), 25 JDIFF("JDiff"), 26 BASELINE("Metalava baseline file", "1.0"), 27 SINCE_XML("Metalava API-level file", "1.0"), 28 29 // signature formats should be last to make comparisons work (for example in [configureOptions]) 30 V1("Doclava signature file", "1.0"), 31 V2("Metalava signature file", "2.0"), 32 V3("Metalava signature file", "3.0"); 33 34 /** Configures the option object such that the output format will be the given format */ configureOptionsnull35 fun configureOptions(options: Options, compatibility: Compatibility) { 36 if (this == JDIFF) { 37 return 38 } 39 options.outputFormat = this 40 options.compatOutput = this == V1 41 options.outputKotlinStyleNulls = this >= V3 42 options.outputDefaultValues = this >= V2 43 compatibility.omitCommonPackages = this >= V2 44 options.includeSignatureFormatVersion = this >= V2 45 } 46 useKotlinStyleNullsnull47 fun useKotlinStyleNulls(): Boolean { 48 return this >= V3 49 } 50 signatureFormatAsIntnull51 private fun signatureFormatAsInt(): Int { 52 return when (this) { 53 V1 -> 1 54 V2 -> 2 55 V3 -> 3 56 57 BASELINE, 58 JDIFF, 59 SINCE_XML, 60 UNKNOWN -> error("this method is only allowed on signature formats, was $this") 61 } 62 } 63 outputFlagnull64 fun outputFlag(): String { 65 return if (isSignatureFormat()) { 66 "$ARG_FORMAT=v${signatureFormatAsInt()}" 67 } else { 68 "" 69 } 70 } 71 preferredExtensionnull72 fun preferredExtension(): String { 73 return when (this) { 74 V1, 75 V2, 76 V3 -> DOT_TXT 77 78 BASELINE -> DOT_TXT 79 80 JDIFF, SINCE_XML -> DOT_XML 81 82 UNKNOWN -> "" 83 } 84 } 85 headernull86 fun header(): String? { 87 val prefix = headerPrefix() ?: return null 88 return prefix + version + "\n" 89 } 90 headerPrefixnull91 private fun headerPrefix(): String? { 92 return when (this) { 93 V1 -> null 94 V2, V3 -> "// Signature format: " 95 BASELINE -> "// Baseline format: " 96 JDIFF, SINCE_XML -> "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" 97 UNKNOWN -> null 98 } 99 } 100 isSignatureFormatnull101 fun isSignatureFormat(): Boolean { 102 return this == V1 || this == V2 || this == V3 103 } 104 105 companion object { firstLinenull106 private fun firstLine(s: String): String { 107 val index = s.indexOf('\n') 108 if (index == -1) { 109 return s 110 } 111 // Chop off \r if a Windows \r\n file 112 val end = if (index > 0 && s[index - 1] == '\r') index - 1 else index 113 return s.substring(0, end) 114 } 115 parseHeadernull116 fun parseHeader(fileContents: String): FileFormat { 117 val firstLine = firstLine(fileContents) 118 for (format in values()) { 119 val header = format.header() 120 if (header == null) { 121 if (firstLine.startsWith("package ")) { 122 // Old signature files 123 return V1 124 } else if (firstLine.startsWith("<api")) { 125 return JDIFF 126 } 127 } else if (header.startsWith(firstLine)) { 128 if (format == JDIFF) { 129 if (!fileContents.contains("<api")) { 130 // The JDIFF header is the general XML header: don't accept XML documents that 131 // don't contain an empty API definition 132 return UNKNOWN 133 } 134 // Both JDiff and API-level files use <api> as the root tag (unfortunate but too late to 135 // change) so distinguish on whether the file contains any since elements 136 if (fileContents.contains("since=")) { 137 return SINCE_XML 138 } 139 } 140 return format 141 } 142 } 143 144 return UNKNOWN 145 } 146 } 147 }