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 package android.processor.compat.changeid; 18 19 import org.w3c.dom.Document; 20 import org.w3c.dom.Element; 21 22 import java.io.IOException; 23 import java.io.OutputStream; 24 25 import javax.xml.parsers.DocumentBuilder; 26 import javax.xml.parsers.DocumentBuilderFactory; 27 import javax.xml.parsers.ParserConfigurationException; 28 import javax.xml.transform.Transformer; 29 import javax.xml.transform.TransformerException; 30 import javax.xml.transform.TransformerFactory; 31 import javax.xml.transform.dom.DOMSource; 32 import javax.xml.transform.stream.StreamResult; 33 34 /** 35 * <p>Writes an XML config file containing provided changes.</p> 36 * <p>Output example:</p> 37 * <pre> 38 * {@code 39 * <config> 40 * <compat-change id="111" name="change-name1"> 41 * <meta-data definedIn="java.package.ClassName" sourcePosition="java/package/ClassName 42 * .java:10" /> 43 * </compat-change> 44 * <compat-change disabled="true" id="222" loggingOnly= "true" name="change-name2" 45 * description="my change"> 46 * <meta-data .../> 47 * </compat-change> 48 * <compat-change enableAfterTargetSdk="28" id="333" name="change-name3"> 49 * <meta-data .../> 50 * </compat-change> 51 * </config> 52 * } 53 * 54 * </pre> 55 * 56 * The inner {@code meta-data} tags are intended to be stripped before embedding the config on a 57 * device. They are intended for use by intermediate build tools only. 58 */ 59 final class XmlWriter { 60 //XML tags 61 private static final String XML_ROOT = "config"; 62 private static final String XML_CHANGE_ELEMENT = "compat-change"; 63 private static final String XML_NAME_ATTR = "name"; 64 private static final String XML_ID_ATTR = "id"; 65 private static final String XML_DISABLED_ATTR = "disabled"; 66 private static final String XML_LOGGING_ATTR = "loggingOnly"; 67 private static final String XML_ENABLED_AFTER_ATTR = "enableAfterTargetSdk"; 68 private static final String XML_DESCRIPTION_ATTR = "description"; 69 private static final String XML_METADATA_ELEMENT = "meta-data"; 70 private static final String XML_DEFINED_IN = "definedIn"; 71 private static final String XML_SOURCE_POSITION = "sourcePosition"; 72 73 private Document mDocument; 74 private Element mRoot; 75 XmlWriter()76 XmlWriter() { 77 mDocument = createDocument(); 78 mRoot = mDocument.createElement(XML_ROOT); 79 mDocument.appendChild(mRoot); 80 } 81 addChange(Change change)82 void addChange(Change change) { 83 Element newElement = mDocument.createElement(XML_CHANGE_ELEMENT); 84 newElement.setAttribute(XML_NAME_ATTR, change.name); 85 newElement.setAttribute(XML_ID_ATTR, change.id.toString()); 86 if (change.disabled) { 87 newElement.setAttribute(XML_DISABLED_ATTR, "true"); 88 } 89 if (change.loggingOnly) { 90 newElement.setAttribute(XML_LOGGING_ATTR, "true"); 91 } 92 if (change.enabledAfter != null) { 93 newElement.setAttribute(XML_ENABLED_AFTER_ATTR, change.enabledAfter.toString()); 94 } 95 if (change.description != null) { 96 newElement.setAttribute(XML_DESCRIPTION_ATTR, change.description); 97 } 98 Element metaData = mDocument.createElement(XML_METADATA_ELEMENT); 99 if (change.qualifiedClass != null) { 100 metaData.setAttribute(XML_DEFINED_IN, change.qualifiedClass); 101 } 102 if (change.sourcePosition != null) { 103 metaData.setAttribute(XML_SOURCE_POSITION, change.sourcePosition); 104 } 105 if (metaData.hasAttributes()) { 106 newElement.appendChild(metaData); 107 } 108 mRoot.appendChild(newElement); 109 } 110 write(OutputStream output)111 void write(OutputStream output) throws IOException { 112 try { 113 TransformerFactory transformerFactory = TransformerFactory.newInstance(); 114 Transformer transformer = transformerFactory.newTransformer(); 115 DOMSource domSource = new DOMSource(mDocument); 116 117 StreamResult result = new StreamResult(output); 118 119 transformer.transform(domSource, result); 120 } catch (TransformerException e) { 121 throw new IOException("Failed to write output", e); 122 } 123 } 124 createDocument()125 private Document createDocument() { 126 try { 127 DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance(); 128 DocumentBuilder documentBuilder = documentFactory.newDocumentBuilder(); 129 return documentBuilder.newDocument(); 130 } catch (ParserConfigurationException e) { 131 throw new RuntimeException("Failed to create a new document", e); 132 } 133 } 134 } 135