1944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis/* 24a360e3a3af230badc847867c117f605367170aaFilip Pavlis * Copyright 2017 The Android Open Source Project 3944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * 4944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * Licensed under the Apache License, Version 2.0 (the "License"); 5944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * you may not use this file except in compliance with the License. 6944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * You may obtain a copy of the License at 7944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * 84a360e3a3af230badc847867c117f605367170aaFilip Pavlis * http://www.apache.org/licenses/LICENSE-2.0 9944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * 10944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * Unless required by applicable law or agreed to in writing, software 11944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * distributed under the License is distributed on an "AS IS" BASIS, 12944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * See the License for the specific language governing permissions and 144a360e3a3af230badc847867c117f605367170aaFilip Pavlis * limitations under the License. 15944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis */ 16944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 17ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlispackage com.android.tools.build.jetifier.processor.transform.pom 18944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 19ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlisimport com.android.tools.build.jetifier.core.pom.PomDependency 20ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlisimport com.android.tools.build.jetifier.core.pom.PomRewriteRule 21ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlisimport com.android.tools.build.jetifier.core.utils.Log 22ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlisimport com.android.tools.build.jetifier.processor.archive.ArchiveFile 23ba381a314edcd57963ed1ac5910595e04faf29ccFilip Pavlisimport com.android.tools.build.jetifier.processor.transform.TransformationContext 24944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlisimport org.jdom2.Document 25944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlisimport org.jdom2.Element 26944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 27944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis/** 28944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * Wraps a single POM XML [ArchiveFile] with parsed metadata about transformation related sections. 29944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis */ 30944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlisclass PomDocument(val file: ArchiveFile, private val document: Document) { 31944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 32944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis companion object { 33944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis private const val TAG = "Pom" 34944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 3560486c575581dcc40605ea91e1062afa30598e36Filip Pavlis fun loadFrom(file: ArchiveFile): PomDocument { 36944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis val document = XmlUtils.createDocumentFromByteArray(file.data) 37944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis val pomDoc = PomDocument(file, document) 38944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis pomDoc.initialize() 39944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis return pomDoc 40944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 41944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 42944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 4360486c575581dcc40605ea91e1062afa30598e36Filip Pavlis val dependencies: MutableSet<PomDependency> = mutableSetOf() 4460486c575581dcc40605ea91e1062afa30598e36Filip Pavlis private val properties: MutableMap<String, String> = mutableMapOf() 4560486c575581dcc40605ea91e1062afa30598e36Filip Pavlis private var dependenciesGroup: Element? = null 4660486c575581dcc40605ea91e1062afa30598e36Filip Pavlis private var hasChanged: Boolean = false 47944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 48944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis private fun initialize() { 49944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis val propertiesGroup = document.rootElement 50944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis .getChild("properties", document.rootElement.namespace) 51944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis if (propertiesGroup != null) { 52944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis propertiesGroup.children 53944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis .filterNot { it.value.isNullOrEmpty() } 54944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis .forEach { properties[it.name] = it.value } 55944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 56944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 57944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis dependenciesGroup = document.rootElement 58944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis .getChild("dependencies", document.rootElement.namespace) ?: return 59944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis dependenciesGroup!!.children.mapTo(dependencies) { 604a360e3a3af230badc847867c117f605367170aaFilip Pavlis XmlUtils.createDependencyFrom(it, properties) 61944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 62944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 63944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 64944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis /** 65944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * Validates that this document is consistent with the provided [rules]. 66944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * 67944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * Currently it checks that all the dependencies that are going to be rewritten by the given 68944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * rules satisfy the minimal version requirements defined by the rules. 69944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis */ 7042ecffe61656f054f9390a61236b1441b659407cFilip Pavlis fun validate(rules: Set<PomRewriteRule>): Boolean { 71944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis if (dependenciesGroup == null) { 72944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis // Nothing to validate as this file has no dependencies section 73944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis return true 74944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 75944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 7690f7af8ae099adad8555b7268c36d072dd139b48Filip Pavlis return dependencies.all { dep -> rules.all { it.validateVersion(dep) } } 77944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 78944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 79944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis /** 80944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * Applies the given [rules] to rewrite the POM file. 81944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * 82944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * Changes are not saved back until requested. 83944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis */ 8414046f89f16795bd405c4f0710e0988a97871e4cFilip Pavlis fun applyRules(context: TransformationContext) { 859462a308f59846b0947a0ca22226f0f4dee51414Filip Pavlis tryRewriteOwnArtifactInfo(context) 860962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis 87944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis if (dependenciesGroup == null) { 88944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis // Nothing to transform as this file has no dependencies section 89944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis return 90944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 91944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 92944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis val newDependencies = mutableSetOf<PomDependency>() 93944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis for (dependency in dependencies) { 9446fd22a7ae48f6952264396ba3932f72c7701de7Filip Pavlis newDependencies.add(mapDependency(dependency, context)) 95944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 96944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 97944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis if (newDependencies.isEmpty()) { 98944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis return 99944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 100944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 101944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis dependenciesGroup!!.children.clear() 102944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis newDependencies.forEach { dependenciesGroup!!.addContent(it.toXmlElement(document)) } 103944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis hasChanged = true 104944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 105944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 1065dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis fun getAsPomDependency(): PomDependency { 1075dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis val groupIdNode = document.rootElement 1085dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis .getChild("groupId", document.rootElement.namespace) 1095dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis val artifactIdNode = document.rootElement 1105dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis .getChild("artifactId", document.rootElement.namespace) 1115dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis val version = document.rootElement 1125dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis .getChild("version", document.rootElement.namespace) 1135dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis 1145dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis return PomDependency(groupIdNode.text, artifactIdNode.text, version.text) 1155dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis } 1165dd5f2ff288862510dc21dfdf4467c88acfd4d52Filip Pavlis 1179462a308f59846b0947a0ca22226f0f4dee51414Filip Pavlis private fun tryRewriteOwnArtifactInfo(context: TransformationContext) { 1180962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis val groupIdNode = document.rootElement 1190962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis .getChild("groupId", document.rootElement.namespace) 1200962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis val artifactIdNode = document.rootElement 1210962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis .getChild("artifactId", document.rootElement.namespace) 1220962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis val version = document.rootElement 1230962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis .getChild("version", document.rootElement.namespace) 1240962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis 1250962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis if (groupIdNode == null || artifactIdNode == null || version == null) { 1260962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis return 1270962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis } 1280962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis 1290962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis val dependency = PomDependency(groupIdNode.text, artifactIdNode.text, version.text) 13046fd22a7ae48f6952264396ba3932f72c7701de7Filip Pavlis val newDependency = mapDependency(dependency, context) 1310962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis 1320962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis if (newDependency != dependency) { 1330962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis groupIdNode.text = newDependency.groupId 1340962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis artifactIdNode.text = newDependency.artifactId 1350962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis version.text = newDependency.version 1369462a308f59846b0947a0ca22226f0f4dee51414Filip Pavlis hasChanged = true 1370962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis } 1380962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis } 1390962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis 1400962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis private fun mapDependency( 1419462a308f59846b0947a0ca22226f0f4dee51414Filip Pavlis dependency: PomDependency, 1429462a308f59846b0947a0ca22226f0f4dee51414Filip Pavlis context: TransformationContext 14346fd22a7ae48f6952264396ba3932f72c7701de7Filip Pavlis ): PomDependency { 1440962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis val rule = context.config.pomRewriteRules.firstOrNull { it.matches(dependency) } 1450962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis if (rule != null) { 1460962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis // Replace with new dependencies 1475b12e59efaab28afee6ffc712a7c8d6ecbae8144Filip Pavlis return rule.to.rewrite(dependency, context.versions) 1480962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis } 1490962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis 1500962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis val matchesPrefix = context.config.restrictToPackagePrefixesWithDots.any { 1510962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis dependency.groupId!!.startsWith(it) 1520962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis } 1530962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis 1540962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis if (matchesPrefix) { 1554e29091e542d87269447fdc8b3254ce86af06845Filip Pavlis context.reportNoPackageMappingFoundFailure( 1564e29091e542d87269447fdc8b3254ce86af06845Filip Pavlis TAG, 1574e29091e542d87269447fdc8b3254ce86af06845Filip Pavlis dependency.toStringNotation(), 1584e29091e542d87269447fdc8b3254ce86af06845Filip Pavlis file.relativePath.toString()) 1590962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis } 1600962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis 1610962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis // No rule to rewrite => keep it 16246fd22a7ae48f6952264396ba3932f72c7701de7Filip Pavlis return dependency 1630962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis } 1640962b3bfd04ef2bb3a52482c83368c4cd28ae73eFilip Pavlis 165944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis /** 166944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * Saves any current pending changes back to the file if needed. 167944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis */ 16890f7af8ae099adad8555b7268c36d072dd139b48Filip Pavlis fun saveBackToFileIfNeeded() { 169944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis if (!hasChanged) { 170944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis return 171944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 172944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 17360486c575581dcc40605ea91e1062afa30598e36Filip Pavlis file.setNewData(XmlUtils.convertDocumentToByteArray(document)) 174944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 175944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis 176944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis /** 177944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis * Logs the information about the current file using info level. 178944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis */ 179944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis fun logDocumentDetails() { 180944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis Log.i(TAG, "POM file at: '%s'", file.relativePath) 181944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis for ((groupId, artifactId, version) in dependencies) { 182448359062b9caf5f96c0028dc12cba5d2647ea6fFilip Pavlis Log.v(TAG, "- Dep: %s:%s:%s", groupId, artifactId, version) 183944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 184944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis } 185944155bbada4741767c149b3f2f6a504cf79cc45Filip Pavlis}